TrianglesConstantBufferExampleComponent.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. // All triangles data will be uploaded in one go to the constant buffer once per frame.
  72. UploadDataToConstantBuffer(trianglesData, static_cast<uint32_t>(sizeof(InstanceInfo)), s_numberOfTrianglesTotal);
  73. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  74. }
  75. void TrianglesConstantBufferExampleComponent::UploadDataToConstantBuffer(InstanceInfo* data, uint32_t elementSize, uint32_t elementCount)
  76. {
  77. AZ::RHI::BufferMapRequest mapRequest;
  78. mapRequest.m_buffer = m_constantBuffer.get();
  79. mapRequest.m_byteCount = elementSize * elementCount;
  80. AZ::RHI::BufferMapResponse mapResponse;
  81. AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
  82. if (resultCode == AZ::RHI::ResultCode::Success)
  83. {
  84. for(const auto& [_, bufferData] : mapResponse.m_data)
  85. {
  86. memcpy(bufferData, data, sizeof(InstanceInfo) * elementCount);
  87. }
  88. m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
  89. }
  90. }
  91. void TrianglesConstantBufferExampleComponent::Activate()
  92. {
  93. using namespace AZ;
  94. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  95. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  96. // Creates Input Assembly buffer and Streams/Index Views
  97. {
  98. m_inputAssemblyBufferPool = aznew RHI::BufferPool();
  99. RHI::BufferPoolDescriptor bufferPoolDesc;
  100. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  101. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  102. m_inputAssemblyBufferPool->Init(bufferPoolDesc);
  103. IABufferData bufferData;
  104. SetVertexPosition(bufferData.m_positions.data(), 0, 0.0f, 0.1f, 0.0);
  105. SetVertexPosition(bufferData.m_positions.data(), 1, -0.1f, -0.1f, 0.0);
  106. SetVertexPosition(bufferData.m_positions.data(), 2, 0.1f, -0.1f, 0.0);
  107. SetVertexColor(bufferData.m_colors.data(), 0, 1.0, 0.0, 0.0, 1.0);
  108. SetVertexColor(bufferData.m_colors.data(), 1, 0.0, 1.0, 0.0, 1.0);
  109. SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0);
  110. SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size());
  111. m_inputAssemblyBuffer = aznew RHI::Buffer();
  112. RHI::BufferInitRequest request;
  113. request.m_buffer = m_inputAssemblyBuffer.get();
  114. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
  115. request.m_initialData = &bufferData;
  116. m_inputAssemblyBufferPool->InitBuffer(request);
  117. m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 3, 0));
  118. m_geometryView.AddStreamBufferView({
  119. *m_inputAssemblyBuffer,
  120. offsetof(IABufferData, m_positions),
  121. sizeof(IABufferData::m_positions),
  122. sizeof(VertexPosition)
  123. });
  124. m_geometryView.AddStreamBufferView({
  125. *m_inputAssemblyBuffer,
  126. offsetof(IABufferData, m_colors),
  127. sizeof(IABufferData::m_colors),
  128. sizeof(VertexColor)
  129. });
  130. m_geometryView.SetIndexBufferView({
  131. *m_inputAssemblyBuffer,
  132. offsetof(IABufferData, m_indices),
  133. sizeof(IABufferData::m_indices),
  134. RHI::IndexFormat::Uint16
  135. });
  136. RHI::InputStreamLayoutBuilder layoutBuilder;
  137. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  138. layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
  139. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  140. RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
  141. }
  142. // Create the buffer pool where both buffers get allocated from
  143. {
  144. m_constantBufferPool = aznew RHI::BufferPool();
  145. RHI::BufferPoolDescriptor constantBufferPoolDesc;
  146. constantBufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant;
  147. constantBufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  148. constantBufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
  149. [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->Init(constantBufferPoolDesc);
  150. AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized constant buffer pool");
  151. }
  152. CreateConstantBufferView();
  153. // Load the Shader and obtain the Pipeline state and its SRG
  154. {
  155. const char* triangeShaderFilePath = "Shaders/RHI/TrianglesConstantBuffer.azshader";
  156. auto shader = LoadShader(triangeShaderFilePath, s_trianglesConstantBufferExampleName);
  157. if (shader == nullptr)
  158. return;
  159. auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  160. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  161. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  162. attachmentsBuilder.AddSubpass()
  163. ->RenderTargetAttachment(m_outputFormat);
  164. [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  165. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  166. m_pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  167. AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state for shader '%s'", triangeShaderFilePath);
  168. m_shaderResourceGroup = CreateShaderResourceGroup(shader, "TriangleSrg", s_trianglesConstantBufferExampleName);
  169. }
  170. // Prepare SRG instance
  171. {
  172. // Find the constant buffer index inside the SRG
  173. const Name trianglesBufferId{ "m_trianglesCB" };
  174. RHI::ShaderInputBufferIndex trianglesBufferIndex;
  175. FindShaderInputIndex(&trianglesBufferIndex, m_shaderResourceGroup, trianglesBufferId, s_trianglesConstantBufferExampleName);
  176. [[maybe_unused]] bool set = m_shaderResourceGroup->SetBufferView(trianglesBufferIndex, m_constantBufferView.get(), 0);
  177. AZ_Assert(set, "Failed to set the buffer view");
  178. // All SRG data has been set already, it only had the buffer view to be set, we can compile now.
  179. // Later then only thing to do is to update the Buffer content itself, but the SRG won't need to be recompiled.
  180. m_shaderResourceGroup->Compile();
  181. }
  182. // Creates a scope for rendering the triangle.
  183. {
  184. struct ScopeData
  185. {
  186. };
  187. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  188. {
  189. // Binds the swap chain as a color attachment. Clears it to white.
  190. {
  191. RHI::ImageScopeAttachmentDescriptor descriptor;
  192. descriptor.m_attachmentId = m_outputAttachmentId;
  193. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  194. frameGraph.UseColorAttachment(descriptor);
  195. }
  196. // We will submit a single draw item.
  197. frameGraph.SetEstimatedItemCount(1);
  198. };
  199. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  200. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  201. {
  202. RHI::CommandList* commandList = context.GetCommandList();
  203. // Set persistent viewport and scissor state.
  204. commandList->SetViewports(&m_viewport, 1);
  205. commandList->SetScissors(&m_scissor, 1);
  206. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  207. m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  208. };
  209. RHI::DeviceDrawItem drawItem;
  210. drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
  211. drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
  212. drawItem.m_drawInstanceArgs = RHI::DrawInstanceArguments(s_numberOfTrianglesTotal, 0);
  213. drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  214. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  215. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  216. // Submit the triangle draw item.
  217. commandList->Submit(drawItem);
  218. };
  219. m_scopeProducers.emplace_back(
  220. aznew RHI::ScopeProducerFunction<
  221. ScopeData,
  222. decltype(prepareFunction),
  223. decltype(compileFunction),
  224. decltype(executeFunction)>(
  225. RHI::ScopeId{"TrianglesConstantBuffer"},
  226. ScopeData{},
  227. prepareFunction,
  228. compileFunction,
  229. executeFunction));
  230. }
  231. TickBus::Handler::BusConnect();
  232. RHI::RHISystemNotificationBus::Handler::BusConnect();
  233. }
  234. void TrianglesConstantBufferExampleComponent::CreateConstantBufferView()
  235. {
  236. using namespace AZ;
  237. const uint32_t constantBufferSize = sizeof(InstanceInfo) * s_numberOfTrianglesTotal;
  238. m_constantBuffer = aznew RHI::Buffer();
  239. RHI::BufferInitRequest request;
  240. request.m_buffer = m_constantBuffer.get();
  241. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::Constant, constantBufferSize };
  242. [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->InitBuffer(request);
  243. AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize constant buffer");
  244. RHI::BufferViewDescriptor bufferDesc = RHI::BufferViewDescriptor::CreateStructured(0, 1u, constantBufferSize);
  245. m_constantBufferView = m_constantBuffer->BuildBufferView(bufferDesc);
  246. }
  247. void TrianglesConstantBufferExampleComponent::Deactivate()
  248. {
  249. using namespace AZ;
  250. m_inputAssemblyBuffer = nullptr;
  251. m_inputAssemblyBufferPool = nullptr;
  252. m_constantBuffer = nullptr;
  253. m_constantBufferView = nullptr;
  254. m_constantBuffer = nullptr;
  255. m_constantBufferPool = nullptr;
  256. m_pipelineState = nullptr;
  257. m_shaderResourceGroup = nullptr;
  258. m_scopeProducers.clear();
  259. m_windowContext = nullptr;
  260. TickBus::Handler::BusConnect();
  261. RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  262. }
  263. } // namespace AtomSampleViewer