TrianglesConstantBufferExampleComponent.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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/TrianglesConstantBufferExampleComponent.h>
  9. #include <Utils/Utils.h>
  10. #include <SampleComponentManager.h>
  11. #include <SampleComponentConfig.h>
  12. #include <Atom/RHI/ScopeProducerFunction.h>
  13. #include <Atom/RHI/CommandList.h>
  14. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  15. #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  16. #include <Atom/RPI.Public/Shader/Shader.h>
  17. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  18. #include <AzCore/Serialization/SerializeContext.h>
  19. namespace AtomSampleViewer
  20. {
  21. const char* TrianglesConstantBufferExampleComponent::s_trianglesConstantBufferExampleName = "TrianglesConstantBufferExample";
  22. const uint32_t TrianglesConstantBufferExampleComponent::s_numberOfTrianglesTotal = 30;
  23. void TrianglesConstantBufferExampleComponent::Reflect(AZ::ReflectContext* context)
  24. {
  25. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  26. {
  27. serializeContext->Class<TrianglesConstantBufferExampleComponent, AZ::Component>()
  28. ->Version(0)
  29. ;
  30. }
  31. }
  32. TrianglesConstantBufferExampleComponent::TrianglesConstantBufferExampleComponent()
  33. {
  34. m_supportRHISamplePipeline = true;
  35. }
  36. void TrianglesConstantBufferExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  37. {
  38. m_time += deltaTime;
  39. }
  40. void TrianglesConstantBufferExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  41. {
  42. using namespace AZ;
  43. InstanceInfo trianglesData[s_numberOfTrianglesTotal];
  44. // Distribute the triangles evenly along the screen
  45. const uint32_t numColumns = static_cast<uint32_t>(ceil(sqrt(s_numberOfTrianglesTotal)));
  46. const uint32_t lastTriangleIndex = s_numberOfTrianglesTotal - 1u;
  47. const uint32_t numRows = (lastTriangleIndex / numColumns) + 1u; // Last triangle's row index plus one is the actual number of rows
  48. for (uint32_t triangleIdx = 0u; triangleIdx < s_numberOfTrianglesTotal; ++triangleIdx)
  49. {
  50. // Location in range [0,1]
  51. const float offsetX = 1.0f / static_cast<float>(numColumns);
  52. const float offsetY = 1.0f / static_cast<float>(numRows);
  53. float x = (triangleIdx % numColumns) * offsetX + 0.5f * offsetX;
  54. float y = (triangleIdx / numColumns) * offsetY + 0.5f * offsetY;
  55. // Location in clip Space [0,1] -> [-1,1]. Triangles located top to bottom, left to right.
  56. x = 2.0f * x - 1.0f;
  57. y = -(2.0f * y - 1.0f); // Finally inverse y so it starts from top to bottom
  58. float time = m_time;
  59. time *= float(triangleIdx + 1u) * 0.2f; // Faster the bigger the index
  60. // Move the triangle around.
  61. AZ::Vector3 translation(
  62. x + sinf(time) * 0.05f,
  63. y + cosf(time) * 0.05f,
  64. 0.0f);
  65. //[GFX_TODO] ATOM-5582 - InstanceInfo should use Atom specific Matrix types
  66. AZ::Matrix4x4 translationMat = AZ::Matrix4x4::CreateTranslation(translation);
  67. translationMat.StoreToRowMajorFloat16(&trianglesData[triangleIdx].m_matrix[0]);
  68. AZ::Color colorMultiplier = fabsf(sinf(time)) * AZ::Color::CreateOne();
  69. colorMultiplier.StoreToFloat4(&trianglesData[triangleIdx].m_colorMultiplier[0]);
  70. }
  71. // Calculate the alignment
  72. const uint32_t alignment = RHI::AlignUp(static_cast<uint32_t>(sizeof(InstanceInfo)), m_constantBufferAlighment);
  73. // All triangles data will be uploaded in one go to the constant buffer once per frame.
  74. UploadDataToConstantBuffer(trianglesData, alignment, s_numberOfTrianglesTotal);
  75. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  76. }
  77. void TrianglesConstantBufferExampleComponent::UploadDataToConstantBuffer(InstanceInfo* data, uint32_t elementSize, uint32_t elementCount)
  78. {
  79. AZ::RHI::BufferMapRequest mapRequest;
  80. mapRequest.m_buffer = m_constantBuffer.get();
  81. mapRequest.m_byteCount = elementSize * elementCount;
  82. AZ::RHI::BufferMapResponse mapResponse;
  83. AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
  84. if (resultCode == AZ::RHI::ResultCode::Success)
  85. {
  86. memcpy(mapResponse.m_data, data, sizeof(InstanceInfo) * elementCount);
  87. m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
  88. }
  89. }
  90. void TrianglesConstantBufferExampleComponent::Activate()
  91. {
  92. using namespace AZ;
  93. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  94. // Cache the alignment
  95. m_constantBufferAlighment = device->GetLimits().m_minConstantBufferViewOffset;
  96. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  97. // Creates Input Assembly buffer and Streams/Index Views
  98. {
  99. m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
  100. RHI::BufferPoolDescriptor bufferPoolDesc;
  101. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  102. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  103. m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
  104. IABufferData bufferData;
  105. SetVertexPosition(bufferData.m_positions.data(), 0, 0.0f, 0.1f, 0.0);
  106. SetVertexPosition(bufferData.m_positions.data(), 1, -0.1f, -0.1f, 0.0);
  107. SetVertexPosition(bufferData.m_positions.data(), 2, 0.1f, -0.1f, 0.0);
  108. SetVertexColor(bufferData.m_colors.data(), 0, 1.0, 0.0, 0.0, 1.0);
  109. SetVertexColor(bufferData.m_colors.data(), 1, 0.0, 1.0, 0.0, 1.0);
  110. SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0);
  111. SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size());
  112. m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
  113. RHI::BufferInitRequest request;
  114. request.m_buffer = m_inputAssemblyBuffer.get();
  115. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
  116. request.m_initialData = &bufferData;
  117. m_inputAssemblyBufferPool->InitBuffer(request);
  118. m_streamBufferViews[0] =
  119. {
  120. *m_inputAssemblyBuffer,
  121. offsetof(IABufferData, m_positions),
  122. sizeof(IABufferData::m_positions),
  123. sizeof(VertexPosition)
  124. };
  125. m_streamBufferViews[1] =
  126. {
  127. *m_inputAssemblyBuffer,
  128. offsetof(IABufferData, m_colors),
  129. sizeof(IABufferData::m_colors),
  130. sizeof(VertexColor)
  131. };
  132. m_indexBufferView =
  133. {
  134. *m_inputAssemblyBuffer,
  135. offsetof(IABufferData, m_indices),
  136. sizeof(IABufferData::m_indices),
  137. RHI::IndexFormat::Uint16
  138. };
  139. RHI::InputStreamLayoutBuilder layoutBuilder;
  140. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  141. layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
  142. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  143. RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
  144. }
  145. // Create the buffer pool where both buffers get allocated from
  146. {
  147. m_constantBufferPool = RHI::Factory::Get().CreateBufferPool();
  148. RHI::BufferPoolDescriptor constantBufferPoolDesc;
  149. constantBufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant;
  150. constantBufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  151. constantBufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
  152. [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->Init(*device, constantBufferPoolDesc);
  153. AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized constant buffer pool");
  154. }
  155. CreateConstantBufferView();
  156. // Load the Shader and obtain the Pipeline state and its SRG
  157. {
  158. const char* triangeShaderFilePath = "Shaders/RHI/TrianglesConstantBuffer.azshader";
  159. auto shader = LoadShader(triangeShaderFilePath, s_trianglesConstantBufferExampleName);
  160. if (shader == nullptr)
  161. return;
  162. auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  163. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  164. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  165. attachmentsBuilder.AddSubpass()
  166. ->RenderTargetAttachment(m_outputFormat);
  167. [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  168. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  169. m_pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  170. AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state for shader '%s'", triangeShaderFilePath);
  171. m_shaderResourceGroup = CreateShaderResourceGroup(shader, "TriangleSrg", s_trianglesConstantBufferExampleName);
  172. }
  173. // Prepare SRG instance
  174. {
  175. // Find the constant buffer index inside the SRG
  176. const Name trianglesBufferId{ "m_trianglesCB" };
  177. RHI::ShaderInputBufferIndex trianglesBufferIndex;
  178. FindShaderInputIndex(&trianglesBufferIndex, m_shaderResourceGroup, trianglesBufferId, s_trianglesConstantBufferExampleName);
  179. [[maybe_unused]] bool set = m_shaderResourceGroup->SetBufferView(trianglesBufferIndex, m_constantBufferView.get(), 0);
  180. AZ_Assert(set, "Failed to set the buffer view");
  181. // All SRG data has been set already, it only had the buffer view to be set, we can compile now.
  182. // Later then only thing to do is to update the Buffer content itself, but the SRG won't need to be recompiled.
  183. m_shaderResourceGroup->Compile();
  184. }
  185. // Creates a scope for rendering the triangle.
  186. {
  187. struct ScopeData
  188. {
  189. };
  190. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  191. {
  192. // Binds the swap chain as a color attachment. Clears it to white.
  193. {
  194. RHI::ImageScopeAttachmentDescriptor descriptor;
  195. descriptor.m_attachmentId = m_outputAttachmentId;
  196. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  197. frameGraph.UseColorAttachment(descriptor);
  198. }
  199. // We will submit a single draw item.
  200. frameGraph.SetEstimatedItemCount(1);
  201. };
  202. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  203. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  204. {
  205. RHI::CommandList* commandList = context.GetCommandList();
  206. // Set persistent viewport and scissor state.
  207. commandList->SetViewports(&m_viewport, 1);
  208. commandList->SetScissors(&m_scissor, 1);
  209. RHI::DrawIndexed drawIndexed;
  210. drawIndexed.m_indexCount = 3;
  211. drawIndexed.m_instanceCount = s_numberOfTrianglesTotal;
  212. const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
  213. RHI::DrawItem drawItem;
  214. drawItem.m_arguments = drawIndexed;
  215. drawItem.m_pipelineState = m_pipelineState.get();
  216. drawItem.m_indexBufferView = &m_indexBufferView;
  217. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  218. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  219. drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
  220. drawItem.m_streamBufferViews = m_streamBufferViews.data();
  221. // Submit the triangle draw item.
  222. commandList->Submit(drawItem);
  223. };
  224. m_scopeProducers.emplace_back(
  225. aznew RHI::ScopeProducerFunction<
  226. ScopeData,
  227. decltype(prepareFunction),
  228. decltype(compileFunction),
  229. decltype(executeFunction)>(
  230. RHI::ScopeId{"TrianglesConstantBuffer"},
  231. ScopeData{},
  232. prepareFunction,
  233. compileFunction,
  234. executeFunction));
  235. }
  236. TickBus::Handler::BusConnect();
  237. RHI::RHISystemNotificationBus::Handler::BusConnect();
  238. }
  239. void TrianglesConstantBufferExampleComponent::CreateConstantBufferView()
  240. {
  241. using namespace AZ;
  242. const uint32_t constantBufferSize = sizeof(InstanceInfo) * s_numberOfTrianglesTotal;
  243. m_constantBuffer = RHI::Factory::Get().CreateBuffer();
  244. RHI::BufferInitRequest request;
  245. request.m_buffer = m_constantBuffer.get();
  246. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::Constant, constantBufferSize };
  247. [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->InitBuffer(request);
  248. AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize constant buffer");
  249. RHI::BufferViewDescriptor bufferDesc = RHI::BufferViewDescriptor::CreateStructured(0, 1u, constantBufferSize);
  250. m_constantBufferView = m_constantBuffer->GetBufferView(bufferDesc);
  251. }
  252. void TrianglesConstantBufferExampleComponent::Deactivate()
  253. {
  254. using namespace AZ;
  255. m_inputAssemblyBuffer = nullptr;
  256. m_inputAssemblyBufferPool = nullptr;
  257. m_constantBuffer = nullptr;
  258. m_constantBufferView = nullptr;
  259. m_constantBuffer = nullptr;
  260. m_constantBufferPool = nullptr;
  261. m_pipelineState = nullptr;
  262. m_shaderResourceGroup = nullptr;
  263. m_scopeProducers.clear();
  264. m_windowContext = nullptr;
  265. TickBus::Handler::BusConnect();
  266. RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  267. }
  268. } // namespace AtomSampleViewer