RayTracingVertexAnimationExampleComponent.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  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 <Performance/RayTracingVertexAnimationExampleComponent.h>
  9. #include <SampleComponentConfig.h>
  10. #include <SampleComponentManager.h>
  11. #include <Atom/Bootstrap/BootstrapNotificationBus.h>
  12. #include <Atom/Component/DebugCamera/ArcBallControllerComponent.h>
  13. #include <Atom/RHI/RayTracingBufferPools.h>
  14. #include <Atom/RPI.Public/Pass/PassFilter.h>
  15. #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
  16. #include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
  17. #include <AzCore/Math/Geometry3DUtils.h>
  18. namespace AZ::RHI
  19. {
  20. using uint = uint32_t;
  21. #include "../../Assets/ShaderLib/Atom/Features/IndirectRayTracing.azsli"
  22. } // namespace AZ::RHI
  23. AZ_DECLARE_BUDGET(AtomSampleViewer);
  24. namespace AZStd
  25. {
  26. template<>
  27. struct hash<AZ::Vector3>
  28. {
  29. using result_type = AZStd::size_t;
  30. result_type operator()(const AZ::Vector3& value) const
  31. {
  32. result_type hash{ 0 };
  33. hash_combine(hash, value.GetX(), value.GetY(), value.GetZ());
  34. return hash;
  35. }
  36. };
  37. } // namespace AZStd
  38. namespace AtomSampleViewer::ImGuiHelper
  39. {
  40. template<typename T, AZStd::enable_if_t<AZStd::is_enum_v<T> && sizeof(T) == sizeof(int), bool> = true>
  41. bool RadioButton(const char* label, T* value, T buttonValue)
  42. {
  43. return ScriptableImGui::RadioButton(label, reinterpret_cast<int*>(value), AZStd::to_underlying(buttonValue));
  44. }
  45. } // namespace AtomSampleViewer::ImGuiHelper
  46. namespace AtomSampleViewer
  47. {
  48. using namespace AZ;
  49. void RayTracingVertexAnimationExampleComponent::Reflect(AZ::ReflectContext* context)
  50. {
  51. if (auto* serializeContext{ azrtti_cast<AZ::SerializeContext*>(context) })
  52. {
  53. serializeContext->Class<RayTracingVertexAnimationExampleComponent, Base>()->Version(0);
  54. }
  55. }
  56. RayTracingVertexAnimationExampleComponent::RayTracingVertexAnimationExampleComponent()
  57. {
  58. m_sampleName = "RayTracingVertexAnimation";
  59. }
  60. void RayTracingVertexAnimationExampleComponent::Activate()
  61. {
  62. InitLightingPresets(true);
  63. auto& rayTracingDebugFeatureProcessor{ GetRayTracingDebugFeatureProcessor() };
  64. rayTracingDebugFeatureProcessor.OnRayTracingDebugComponentAdded();
  65. rayTracingDebugFeatureProcessor.GetSettingsInterface()->SetDebugViewMode(m_rayTracingDebugViewMode);
  66. EBUS_EVENT_ID(GetCameraEntityId(), Debug::CameraControllerRequestBus, Enable, azrtti_typeid<Debug::ArcBallControllerComponent>());
  67. EBUS_EVENT_ID(GetCameraEntityId(), Debug::ArcBallControllerRequestBus, SetDistance, 10.f);
  68. EBUS_EVENT_ID(GetCameraEntityId(), Debug::ArcBallControllerRequestBus, SetPitch, DegToRad(-20.f));
  69. EBUS_EVENT_ID(GetCameraEntityId(), Debug::ArcBallControllerRequestBus, SetPan, AZ::Vector3{ 0.2f, 0.6f, 1.2f });
  70. GeneratePerformanceConfigurations();
  71. CreateBufferPools();
  72. CreateRayTracingGeometry();
  73. m_imguiSidebar.Activate();
  74. SaveVSyncStateAndDisableVsync();
  75. RPI::SceneNotificationBus::Handler::BusConnect(RPI::Scene::GetSceneForEntityContextId(GetEntityContextId())->GetId());
  76. AZ::TickBus::Handler::BusConnect();
  77. Render::Bootstrap::NotificationBus::Broadcast(&Render::Bootstrap::NotificationBus::Handler::OnBootstrapSceneReady, m_scene);
  78. }
  79. void RayTracingVertexAnimationExampleComponent::Deactivate()
  80. {
  81. AZ::TickBus::Handler::BusDisconnect();
  82. RPI::SceneNotificationBus::Handler::BusDisconnect();
  83. RestoreVSyncState();
  84. m_rayTracingAccelerationStructurePass->SetTimestampQueryEnabled(false);
  85. m_rayTracingAccelerationStructurePass.reset();
  86. m_debugRayTracingPass->SetTimestampQueryEnabled(false);
  87. m_debugRayTracingPass.reset();
  88. m_imguiSidebar.Deactivate();
  89. for (const RayTracingMesh& rayTracingMesh : m_rayTracingData)
  90. {
  91. GetRayTracingFeatureProcessor().RemoveMesh(rayTracingMesh.m_uuid);
  92. for (const auto& subMesh : rayTracingMesh.m_rtSubMeshes)
  93. {
  94. GetMeshFeatureProcessor().ReleaseMeshInfoEntry(subMesh.m_meshInfoHandle);
  95. }
  96. }
  97. GetRayTracingDebugFeatureProcessor().OnRayTracingDebugComponentRemoved();
  98. ShutdownLightingPresets();
  99. }
  100. void RayTracingVertexAnimationExampleComponent::OnTick(float deltaTime, AZ::ScriptTimePoint /*timePoint*/)
  101. {
  102. m_imGuiFrameTimer.PushValue(deltaTime);
  103. m_accelerationStructurePassTimer.PushValue(
  104. m_rayTracingAccelerationStructurePass->GetLatestTimestampResult().GetDurationInNanoseconds() / 1'000'000'000.f);
  105. m_rayTracingPassTimer.PushValue(m_debugRayTracingPass->GetLatestTimestampResult().GetDurationInNanoseconds() / 1'000'000'000.f);
  106. DrawSidebar();
  107. UpdatePerformanceData();
  108. }
  109. void RayTracingVertexAnimationExampleComponent::UpdatePerformanceData()
  110. {
  111. if (m_currentPerformanceConfiguration == -1)
  112. {
  113. return;
  114. }
  115. if (m_measureTicks == 0)
  116. {
  117. const auto& currentConfiguration{ m_performanceConfigurations[m_currentPerformanceConfiguration] };
  118. m_accelerationStructureType = currentConfiguration.m_accelerationStructureType;
  119. m_geometryCount = currentConfiguration.m_geometryCount;
  120. CreateRayTracingGeometry();
  121. }
  122. else if (m_measureTicks == aznumeric_cast<int>(m_histogramSampleCount) * 3)
  123. {
  124. // (histogram size * 3) seems to be long enough for the measured values to become reasonable stable
  125. m_performanceResults.push_back(
  126. { m_accelerationStructurePassTimer.GetDisplayedAverage(), m_rayTracingPassTimer.GetDisplayedAverage() });
  127. m_measureTicks = -1;
  128. m_currentPerformanceConfiguration++;
  129. if (m_currentPerformanceConfiguration == m_performanceConfigurations.size())
  130. {
  131. PrintPerformanceResults();
  132. m_currentPerformanceConfiguration = -1;
  133. }
  134. }
  135. m_measureTicks++;
  136. }
  137. void RayTracingVertexAnimationExampleComponent::OnRenderPipelineChanged(
  138. AZ::RPI::RenderPipeline* renderPipeline, RenderPipelineChangeType changeType)
  139. {
  140. AZ_UNUSED(changeType);
  141. if (!renderPipeline->GetScene() || renderPipeline != renderPipeline->GetScene()->GetDefaultRenderPipeline().get())
  142. {
  143. return;
  144. }
  145. auto meshFeatureProcessor{ AZ::RPI::Scene::GetFeatureProcessorForEntityContextId<Render::MeshFeatureProcessorInterface>(
  146. GetEntityContextId()) };
  147. AZ::RPI::PassFilter accelerationStructurePassFilter{ AZ::RPI::PassFilter::CreateWithPassName(
  148. AZ::Name{ "RayTracingAccelerationStructurePass" }, meshFeatureProcessor->GetParentScene()) };
  149. m_rayTracingAccelerationStructurePass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(accelerationStructurePassFilter);
  150. m_rayTracingAccelerationStructurePass->SetTimestampQueryEnabled(true);
  151. AZ::RPI::PassFilter rayTracingPassFilter{ AZ::RPI::PassFilter::CreateWithPassName(
  152. AZ::Name{ "DebugRayTracingPass" }, meshFeatureProcessor->GetParentScene()) };
  153. m_debugRayTracingPass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(rayTracingPassFilter);
  154. if (m_debugRayTracingPass) // The debug ray tracing pass is not available in the first invocation of OnRenderPipelineChanged
  155. {
  156. m_debugRayTracingPass->SetTimestampQueryEnabled(true);
  157. }
  158. AddVertexAnimationPass(renderPipeline);
  159. }
  160. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetTriangleCount() const
  161. {
  162. return GetIndexCount() / 3;
  163. }
  164. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetTriangleCountPerCluster() const
  165. {
  166. return m_trianglesPerCluster;
  167. }
  168. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetIndexCount() const
  169. {
  170. return aznumeric_cast<int>(m_indices.size());
  171. }
  172. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetIndexCountPerCluster() const
  173. {
  174. return m_trianglesPerCluster * 3;
  175. }
  176. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetVertexCount() const
  177. {
  178. return aznumeric_cast<int>(m_positions.size());
  179. }
  180. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetVertexCountPerCluster() const
  181. {
  182. return m_verticesPerCluster;
  183. }
  184. int RayTracingVertexAnimationExampleComponent::BasicGeometry::GetClusterCount() const
  185. {
  186. return GetTriangleCount() / GetTriangleCountPerCluster();
  187. }
  188. void RayTracingVertexAnimationExampleComponent::SaveVSyncStateAndDisableVsync()
  189. {
  190. AzFramework::NativeWindowHandle windowHandle{ nullptr };
  191. EBUS_EVENT_RESULT(windowHandle, AzFramework::WindowSystemRequestBus, GetDefaultWindowHandle);
  192. EBUS_EVENT_ID_RESULT(m_preActivateVSyncInterval, windowHandle, AzFramework::WindowRequestBus, GetSyncInterval);
  193. EBUS_EVENT_ID(windowHandle, AzFramework::WindowRequestBus, SetSyncInterval, 0);
  194. }
  195. void RayTracingVertexAnimationExampleComponent::RestoreVSyncState()
  196. {
  197. AzFramework::NativeWindowHandle windowHandle{ nullptr };
  198. EBUS_EVENT_RESULT(windowHandle, AzFramework::WindowSystemRequestBus, GetDefaultWindowHandle);
  199. EBUS_EVENT_ID(windowHandle, AzFramework::WindowRequestBus, SetSyncInterval, m_preActivateVSyncInterval);
  200. }
  201. void RayTracingVertexAnimationExampleComponent::GeneratePerformanceConfigurations()
  202. {
  203. m_performanceConfigurations.clear();
  204. m_performanceResults.clear();
  205. auto addPerformanceConfiguration{
  206. [&](int geometryCount)
  207. {
  208. m_performanceConfigurations.push_back({ geometryCount, AccelerationStructureType::TriangleBLAS });
  209. m_performanceConfigurations.push_back({ geometryCount, AccelerationStructureType::CLAS_ClusterBLAS });
  210. }
  211. };
  212. addPerformanceConfiguration(1);
  213. addPerformanceConfiguration(5);
  214. addPerformanceConfiguration(10);
  215. addPerformanceConfiguration(30);
  216. addPerformanceConfiguration(60);
  217. addPerformanceConfiguration(100);
  218. }
  219. RayTracingVertexAnimationExampleComponent::BasicGeometry RayTracingVertexAnimationExampleComponent::GenerateBasicGeometry()
  220. {
  221. constexpr int triangleCountPerCluster{ 64 };
  222. constexpr int indexCountPerCluster{ triangleCountPerCluster * 3 };
  223. AZStd::vector<AZ::Vector3> vertices{ Geometry3dUtils::GenerateIcoSphere(6) }; // 6 subdivisions -> 81920 triangles
  224. int clusterCount{ aznumeric_cast<int>(vertices.size()) / indexCountPerCluster };
  225. BasicGeometry geometry;
  226. int maxVertexCountPerCluster{ 0 };
  227. for (int clusterIndex{ 0 }; clusterIndex < clusterCount; clusterIndex++)
  228. {
  229. u32 vertexOffset{ aznumeric_cast<u32>(geometry.m_positions.size()) };
  230. AZStd::unordered_map<AZ::Vector3, u32> uniqueVertices;
  231. AZStd::map<u32, AZ::Vector3> aggregatedVertexNormals; // Use map to automatically sort by vertex id
  232. for (int i{ 0 }; i < indexCountPerCluster; i += 3)
  233. {
  234. int baseIndex{ clusterIndex * indexCountPerCluster + i };
  235. AZ::Vector3 faceNormal{
  236. (vertices[baseIndex + 1] - vertices[baseIndex]).Cross(vertices[baseIndex + 2] - vertices[baseIndex]).GetNormalized()
  237. };
  238. for (int j{ 0 }; j < 3; j++)
  239. {
  240. auto [iterator, inserted]{ uniqueVertices.emplace(
  241. vertices[baseIndex + j], aznumeric_cast<u32>(uniqueVertices.size())) };
  242. if (inserted)
  243. {
  244. geometry.m_positions.emplace_back(iterator->first);
  245. }
  246. geometry.m_indices.emplace_back(vertexOffset + iterator->second);
  247. aggregatedVertexNormals[iterator->second] += faceNormal;
  248. }
  249. }
  250. for (const auto& [_, vertexNormal] : aggregatedVertexNormals)
  251. {
  252. geometry.m_normals.emplace_back(vertexNormal.GetNormalized());
  253. }
  254. maxVertexCountPerCluster = AZStd::max(maxVertexCountPerCluster, aznumeric_cast<int>(uniqueVertices.size()));
  255. }
  256. geometry.m_trianglesPerCluster = triangleCountPerCluster;
  257. geometry.m_verticesPerCluster = maxVertexCountPerCluster;
  258. return geometry;
  259. }
  260. void RayTracingVertexAnimationExampleComponent::CreateBufferPools()
  261. {
  262. auto targetBufferPoolDesc{ AZStd::make_unique<AZ::RHI::BufferPoolDescriptor>() };
  263. targetBufferPoolDesc->m_bindFlags = m_geometryDataBufferBindFlags;
  264. targetBufferPoolDesc->m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
  265. targetBufferPoolDesc->m_hostMemoryAccess = AZ::RHI::HostMemoryAccess::Write;
  266. AZ::RPI::ResourcePoolAssetCreator targetBufferPoolCreator;
  267. targetBufferPoolCreator.Begin(AZ::Uuid::CreateRandom());
  268. targetBufferPoolCreator.SetPoolDescriptor(AZStd::move(targetBufferPoolDesc));
  269. targetBufferPoolCreator.SetPoolName("RayTracingExampleComponentTargetGeometry-BufferPool");
  270. targetBufferPoolCreator.End(m_geometryDataBufferPoolAsset);
  271. }
  272. void RayTracingVertexAnimationExampleComponent::CreateRayTracingGeometry()
  273. {
  274. for (const RayTracingMesh& rayTracingMesh : m_rayTracingData)
  275. {
  276. GetRayTracingFeatureProcessor().RemoveMesh(rayTracingMesh.m_uuid);
  277. for (const auto& subMesh : rayTracingMesh.m_rtSubMeshes)
  278. {
  279. GetMeshFeatureProcessor().ReleaseMeshInfoEntry(subMesh.m_meshInfoHandle);
  280. }
  281. }
  282. m_rayTracingData.clear();
  283. constexpr AZ::RHI::VertexFormat PositionStreamFormat{ AZ::RHI::VertexFormat::R32G32B32_FLOAT };
  284. constexpr AZ::RHI::VertexFormat NormalStreamFormat{ AZ::RHI::VertexFormat::R32G32B32_FLOAT };
  285. constexpr AZ::RHI::IndexFormat IndexStreamFormat{ AZ::RHI::IndexFormat::Uint32 };
  286. const uint32_t PositionSize{ AZ::RHI::GetVertexFormatSize(PositionStreamFormat) };
  287. const uint32_t NormalSize{ AZ::RHI::GetVertexFormatSize(NormalStreamFormat) };
  288. const uint32_t IndexSize{ AZ::RHI::GetIndexFormatSize(IndexStreamFormat) };
  289. BasicGeometry geometry{ GenerateBasicGeometry() };
  290. int vertexCount{ geometry.GetVertexCount() };
  291. int indexCount{ geometry.GetIndexCount() };
  292. u32 positionByteOffset{ 0 };
  293. u32 normalByteOffset{ positionByteOffset + vertexCount * PositionSize };
  294. u32 indexByteOffset{ normalByteOffset + vertexCount * NormalSize };
  295. u32 sourceBufferSize{ AZ::SizeAlignUp(indexByteOffset + indexCount * IndexSize, PositionSize) };
  296. u32 targetBufferSizePerInstance{ vertexCount * PositionSize };
  297. u32 targetBufferSize{ targetBufferSizePerInstance * m_geometryCount };
  298. u32 targetBufferSizePerCluster{ geometry.GetVertexCountPerCluster() * PositionSize };
  299. u32 indexBufferSizePerCluster{ geometry.GetIndexCountPerCluster() * IndexSize };
  300. m_vertexCountPerInstance = vertexCount;
  301. m_targetVertexStridePerInstance = targetBufferSizePerInstance / PositionSize;
  302. AZStd::vector<AZStd::byte> geometryBuffer(sourceBufferSize);
  303. memcpy(geometryBuffer.data() + positionByteOffset, geometry.m_positions.data(), vertexCount * PositionSize);
  304. memcpy(geometryBuffer.data() + normalByteOffset, geometry.m_normals.data(), vertexCount * NormalSize);
  305. memcpy(geometryBuffer.data() + indexByteOffset, geometry.m_indices.data(), indexCount * IndexSize);
  306. if (!m_sourceGeometryBuffer)
  307. {
  308. AZ::RPI::BufferAssetCreator sourceBufferCreator;
  309. sourceBufferCreator.Begin(AZ::Uuid::CreateRandom());
  310. sourceBufferCreator.SetBufferName("RayTracingExampleComponentSourceGeometry");
  311. sourceBufferCreator.SetPoolAsset(m_geometryDataBufferPoolAsset);
  312. sourceBufferCreator.SetBuffer(
  313. geometryBuffer.data(), sourceBufferSize, AZ::RHI::BufferDescriptor{ m_geometryDataBufferBindFlags, sourceBufferSize });
  314. sourceBufferCreator.SetBufferViewDescriptor(AZ::RHI::BufferViewDescriptor::CreateStructured(0, sourceBufferSize, 1));
  315. AZ::Data::Asset<AZ::RPI::BufferAsset> sourceBufferAsset;
  316. sourceBufferCreator.End(sourceBufferAsset);
  317. m_sourceGeometryBuffer = AZ::RPI::Buffer::FindOrCreate(sourceBufferAsset);
  318. }
  319. {
  320. AZ::RPI::BufferAssetCreator targetBufferCreator;
  321. targetBufferCreator.Begin(AZ::Uuid::CreateRandom());
  322. targetBufferCreator.SetBufferName("RayTracingExampleComponentTargetGeometry");
  323. targetBufferCreator.SetPoolAsset(m_geometryDataBufferPoolAsset);
  324. targetBufferCreator.SetBuffer(nullptr, 0, AZ::RHI::BufferDescriptor{ m_geometryDataBufferBindFlags, targetBufferSize });
  325. targetBufferCreator.SetBufferViewDescriptor(AZ::RHI::BufferViewDescriptor::CreateStructured(0, targetBufferSize, 1));
  326. AZ::Data::Asset<AZ::RPI::BufferAsset> targetBufferAsset;
  327. targetBufferCreator.End(targetBufferAsset);
  328. m_targetGeometryBuffer = AZ::RPI::Buffer::FindOrCreate(targetBufferAsset);
  329. }
  330. auto sourceRhiGeometryBuffer{ m_sourceGeometryBuffer->GetRHIBuffer() };
  331. auto targetRhiGeometryBuffer{ m_targetGeometryBuffer->GetRHIBuffer() };
  332. {
  333. int gridWidth{ aznumeric_cast<int>(AZStd::ceil(AZStd::sqrt(m_geometryCount))) };
  334. float gridSpacing{ 2.3f };
  335. AZStd::vector<AZ::PackedVector3f> instanceOffsets;
  336. for (int i{ 0 }; i < m_geometryCount; i++)
  337. {
  338. instanceOffsets.emplace_back((i % gridWidth - (gridWidth - 1) / 2.f) * gridSpacing, (i / gridWidth) * gridSpacing, 0.f);
  339. }
  340. RPI::CommonBufferDescriptor instanceOffsetBufferDescriptor;
  341. instanceOffsetBufferDescriptor.m_poolType = RPI::CommonBufferPoolType::ReadWrite;
  342. instanceOffsetBufferDescriptor.m_bufferName = "InstanceOffsetBuffer";
  343. instanceOffsetBufferDescriptor.m_byteCount = instanceOffsets.size() * sizeof(instanceOffsets[0]);
  344. instanceOffsetBufferDescriptor.m_bufferData = instanceOffsets.data();
  345. m_instanceOffsetDataBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(instanceOffsetBufferDescriptor);
  346. }
  347. if (m_vertexAnimationPass)
  348. {
  349. SetVertexAnimationPassData();
  350. }
  351. if (m_accelerationStructureType == AccelerationStructureType::TriangleBLAS)
  352. {
  353. for (int i{ 0 }; i < m_geometryCount; i++)
  354. {
  355. auto& data{ m_rayTracingData.emplace_back() };
  356. data.m_uuid = AZ::Uuid::CreateRandom();
  357. // m_assetId needs to be unique for each instance, otherwise BLASes are not unique
  358. data.m_rtMesh.m_assetId = Data::AssetId{ Uuid::CreateRandom() };
  359. data.m_rtMesh.m_isSkinnedMesh = true; // Skinned mesh BLASes are updated every frame
  360. data.m_rtMesh.m_instanceMask |=
  361. static_cast<uint32_t>(AZ::RHI::RayTracingAccelerationStructureInstanceInclusionMask::STATIC_MESH);
  362. auto& subMesh{ data.m_rtSubMeshes.emplace_back() };
  363. subMesh.m_meshInfoHandle = GetMeshFeatureProcessor().AcquireMeshInfoEntry();
  364. GetMeshFeatureProcessor().UpdateMeshInfoEntry(
  365. subMesh.m_meshInfoHandle,
  366. [&](Render::MeshInfoEntry* meshInfoEntry)
  367. {
  368. // Use index data from source data;
  369. meshInfoEntry->m_indexBuffer = Render::IndexBufferViewIndexAndOffset::Create(
  370. RHI::IndexBufferView{ *sourceRhiGeometryBuffer, indexByteOffset, indexCount * IndexSize, IndexStreamFormat });
  371. // Use position data from target buffer with unique offset per instance
  372. meshInfoEntry->m_meshBuffers[RHI::ShaderSemantic{ "POSITION" }] = Render::BufferViewIndexAndOffset::Create(
  373. RHI::StreamBufferView{ *targetRhiGeometryBuffer, targetBufferSizePerInstance * i, vertexCount * PositionSize,
  374. PositionSize },
  375. PositionStreamFormat);
  376. // Use normal data from source buffer
  377. meshInfoEntry->m_meshBuffers[RHI::ShaderSemantic{ "NORMAL" }] = Render::BufferViewIndexAndOffset::Create(
  378. RHI::StreamBufferView{ *sourceRhiGeometryBuffer, normalByteOffset, vertexCount * NormalSize, NormalSize },
  379. NormalStreamFormat);
  380. return true;
  381. });
  382. // Dont need to set material since the DebugRayTracingPass is used to visualize the geometry
  383. GetRayTracingFeatureProcessor().AddMesh(data.m_uuid, data.m_rtMesh, data.m_rtSubMeshes);
  384. }
  385. }
  386. else if (m_accelerationStructureType == AccelerationStructureType::CLAS_ClusterBLAS)
  387. {
  388. auto& data{ m_rayTracingData.emplace_back() };
  389. data.m_uuid = AZ::Uuid::CreateRandom();
  390. // m_assetId needs to be unique for each instance, otherwise BLASes are not unique
  391. data.m_rtMesh.m_assetId = Data::AssetId{ Uuid::CreateRandom() };
  392. data.m_rtMesh.m_isSkinnedMesh = true; // Skinned mesh BLASes are updated every frame
  393. data.m_rtMesh.m_instanceMask |=
  394. static_cast<uint32_t>(AZ::RHI::RayTracingAccelerationStructureInstanceInclusionMask::STATIC_MESH);
  395. auto updateMeshInfoBuffers{
  396. [&](const Render::MeshInfoHandle& meshInfoHandle, const RHI::Ptr<RHI::BufferView>& clusterOffsetBufferView)
  397. {
  398. GetMeshFeatureProcessor().UpdateMeshInfoEntry(
  399. meshInfoHandle,
  400. [&](Render::MeshInfoEntry* meshInfoEntry)
  401. {
  402. // Use index data from source data;
  403. meshInfoEntry->m_indexBuffer = Render::IndexBufferViewIndexAndOffset::Create(
  404. RHI::IndexBufferView{ *sourceRhiGeometryBuffer, indexByteOffset, indexCount * IndexSize,
  405. IndexStreamFormat });
  406. // Use position data from target buffer with unique offset per instance
  407. meshInfoEntry->m_meshBuffers[RHI::ShaderSemantic{ "POSITION" }] = Render::BufferViewIndexAndOffset::Create(
  408. RHI::StreamBufferView{ *targetRhiGeometryBuffer, 0, vertexCount * PositionSize, PositionSize },
  409. PositionStreamFormat);
  410. // Use normal data from source buffer
  411. meshInfoEntry->m_meshBuffers[RHI::ShaderSemantic{ "NORMAL" }] = Render::BufferViewIndexAndOffset::Create(
  412. RHI::StreamBufferView{ *sourceRhiGeometryBuffer, normalByteOffset, vertexCount * NormalSize, NormalSize },
  413. NormalStreamFormat);
  414. meshInfoEntry->m_clusterOffsetBuffer = clusterOffsetBufferView;
  415. return true;
  416. });
  417. }
  418. };
  419. auto& bufferPools{ GetRayTracingFeatureProcessor().GetBufferPools() };
  420. int clusterCountPerInstance{ geometry.GetClusterCount() };
  421. int totalClusterCount{ m_geometryCount * clusterCountPerInstance };
  422. {
  423. m_srcInfosArrayBuffer = aznew RHI::Buffer();
  424. m_srcInfosArrayBuffer->SetName(Name("SourceInfosArrayBuffer"));
  425. RHI::BufferInitRequest srcInfoInitRequest;
  426. srcInfoInitRequest.m_buffer = m_srcInfosArrayBuffer.get();
  427. srcInfoInitRequest.m_descriptor.m_byteCount = totalClusterCount * sizeof(RHI::RayTracingClasBuildTriangleClusterInfo);
  428. srcInfoInitRequest.m_descriptor.m_bindFlags = bufferPools.GetSrcInfosArrayBufferPool()->GetDescriptor().m_bindFlags;
  429. bufferPools.GetSrcInfosArrayBufferPool()->InitBuffer(srcInfoInitRequest);
  430. }
  431. {
  432. m_clusterStreamOffsets = aznew RHI::Buffer();
  433. m_clusterStreamOffsets->SetName(Name("ClusterStreamOffsetsBuffer"));
  434. RHI::BufferInitRequest clusterStreamOffsetsInitRequest;
  435. clusterStreamOffsetsInitRequest.m_buffer = m_clusterStreamOffsets.get();
  436. clusterStreamOffsetsInitRequest.m_descriptor.m_byteCount = totalClusterCount * sizeof(RHI::RayTracingClasClusterOffsetInfo);
  437. clusterStreamOffsetsInitRequest.m_descriptor.m_bindFlags =
  438. bufferPools.GetSrcInfosArrayBufferPool()->GetDescriptor().m_bindFlags;
  439. bufferPools.GetSrcInfosArrayBufferPool()->InitBuffer(clusterStreamOffsetsInitRequest);
  440. }
  441. const auto targetBufferAddress{ targetRhiGeometryBuffer->GetDeviceAddress() };
  442. const auto sourceBufferAddress{ sourceRhiGeometryBuffer->GetDeviceAddress() };
  443. RHI::BufferMapRequest srcInfoMapRequest;
  444. srcInfoMapRequest.m_buffer = m_srcInfosArrayBuffer.get();
  445. srcInfoMapRequest.m_byteCount = m_srcInfosArrayBuffer->GetDescriptor().m_byteCount;
  446. RHI::BufferMapResponse srcInfoMapResponse;
  447. bufferPools.GetSrcInfosArrayBufferPool()->MapBuffer(srcInfoMapRequest, srcInfoMapResponse);
  448. RHI::BufferMapRequest clusterStreamOffsetsMapRequest;
  449. clusterStreamOffsetsMapRequest.m_buffer = m_clusterStreamOffsets.get();
  450. clusterStreamOffsetsMapRequest.m_byteCount = m_clusterStreamOffsets->GetDescriptor().m_byteCount;
  451. RHI::BufferMapResponse clusterStreamOffsetsMapResponse;
  452. bufferPools.GetSrcInfosArrayBufferPool()->MapBuffer(clusterStreamOffsetsMapRequest, clusterStreamOffsetsMapResponse);
  453. for (int i{ 0 }; i < totalClusterCount; i++)
  454. {
  455. int clusterId{ i % clusterCountPerInstance };
  456. // This data should generally be filled in on the GPU, but since the cluster parameters do not change in this case, it would
  457. // not benefit the sample
  458. RHI::RayTracingClasBuildTriangleClusterInfoExpanded clusterInfoExpanded{};
  459. clusterInfoExpanded.m_clusterID = clusterId;
  460. clusterInfoExpanded.m_clusterFlags = AZ::RHI::RayTracingClasClusterFlags::AllowDisableOpacityMicromaps;
  461. clusterInfoExpanded.m_triangleCount = geometry.GetTriangleCountPerCluster();
  462. clusterInfoExpanded.m_vertexCount = geometry.GetVertexCountPerCluster(); // TODO(CLAS): Unique vertex count per cluster?
  463. clusterInfoExpanded.m_positionTruncateBitCount = 0;
  464. clusterInfoExpanded.m_indexType = AZ::RHI::RayTracingClasIndexFormat::UINT32;
  465. clusterInfoExpanded.m_opacityMicromapIndexType = AZ::RHI::RayTracingClasIndexFormat::UINT32;
  466. // Using a unique geometry index per cluster also entails adding a unique hit group record in the ray tracing pass
  467. clusterInfoExpanded.m_baseGeometryIndex = 0; // TODO(CLAS): Investigate unique per-cluster geometry index
  468. clusterInfoExpanded.m_geometryFlags = AZ::RHI::RayTracingClasGeometryFlags::Opaque;
  469. clusterInfoExpanded.m_indexBufferStride = 0;
  470. clusterInfoExpanded.m_vertexBufferStride = 0;
  471. clusterInfoExpanded.m_geometryIndexAndFlagsBufferStride = 0;
  472. clusterInfoExpanded.m_opacityMicromapIndexBufferStride = 0;
  473. clusterInfoExpanded.m_indexBufferAddress = 0;
  474. clusterInfoExpanded.m_vertexBufferAddress = 0;
  475. clusterInfoExpanded.m_geometryIndexAndFlagsBufferAddress = 0;
  476. clusterInfoExpanded.m_opacityMicromapArrayAddress = 0;
  477. clusterInfoExpanded.m_opacityMicromapIndexBufferAddress = 0;
  478. for (const auto& [deviceIndex, dataPointer] : srcInfoMapResponse.m_data)
  479. {
  480. // The vertex data is unique for each cluster (output of VertexAnimationPass)
  481. clusterInfoExpanded.m_vertexBufferAddress = targetBufferAddress.at(deviceIndex) + targetBufferSizePerCluster * i;
  482. // The index data is the same for all clusters (Since the indices do not restart at 0 for each cluster, there is no
  483. // additional per-cluster offset)
  484. clusterInfoExpanded.m_indexBufferAddress = sourceBufferAddress.at(deviceIndex) + indexByteOffset;
  485. auto* gpuClusterInfo{ reinterpret_cast<RHI::RayTracingClasBuildTriangleClusterInfo*>(dataPointer) + i };
  486. *gpuClusterInfo = RHI::RayTracingClasConvertBuildTriangleClusterInfo(clusterInfoExpanded);
  487. }
  488. for (const auto& [deviceIndex, dataPointer] : clusterStreamOffsetsMapResponse.m_data)
  489. {
  490. auto* clusterOffsetInfo{ reinterpret_cast<RHI::RayTracingClasClusterOffsetInfo*>(dataPointer) + i };
  491. // Use same index data for all clusters
  492. clusterOffsetInfo->m_indexOffset = indexByteOffset + indexBufferSizePerCluster * clusterId;
  493. clusterOffsetInfo->m_indexStride = RHI::GetIndexFormatSize(IndexStreamFormat); // Tightly pack indices
  494. // Use unique position data for each cluster
  495. clusterOffsetInfo->m_positionOffset = positionByteOffset + targetBufferSizePerCluster * i;
  496. clusterOffsetInfo->m_positionStride = RHI::GetVertexFormatSize(PositionStreamFormat); // Tightly pack positions
  497. // Use same normal data for all clusters
  498. clusterOffsetInfo->m_normalOffset = normalByteOffset;
  499. clusterOffsetInfo->m_normalStride = RHI::GetVertexFormatSize(NormalStreamFormat); // Tightly pack normals
  500. }
  501. }
  502. bufferPools.GetSrcInfosArrayBufferPool()->UnmapBuffer(*m_srcInfosArrayBuffer);
  503. bufferPools.GetSrcInfosArrayBufferPool()->UnmapBuffer(*m_clusterStreamOffsets);
  504. RHI::RayTracingClusterBlasDescriptor clusterDescriptor;
  505. clusterDescriptor.m_buildFlags = AZ::RHI::RayTracingAccelerationStructureBuildFlags::FAST_BUILD;
  506. clusterDescriptor.m_vertexFormat = AZ::RHI::VertexFormat::R32G32B32_FLOAT;
  507. clusterDescriptor.m_maxGeometryIndexValue = 0;
  508. clusterDescriptor.m_maxClusterUniqueGeometryCount = 1;
  509. clusterDescriptor.m_maxClusterTriangleCount = geometry.GetTriangleCountPerCluster();
  510. clusterDescriptor.m_maxClusterVertexCount = geometry.GetVertexCountPerCluster();
  511. clusterDescriptor.m_maxTotalTriangleCount = geometry.GetTriangleCount();
  512. clusterDescriptor.m_maxTotalVertexCount = geometry.GetVertexCount();
  513. clusterDescriptor.m_maxClusterCount = clusterCountPerInstance;
  514. clusterDescriptor.m_minPositionTruncateBitCount = 0;
  515. // clusterDescriptor.m_srcInfosCountBufferView: Dont use this buffer in the sample
  516. if (!m_separateClusterBlasForEachInstance)
  517. {
  518. clusterDescriptor.m_maxTotalTriangleCount *= m_geometryCount;
  519. clusterDescriptor.m_maxTotalVertexCount *= m_geometryCount;
  520. clusterDescriptor.m_maxClusterCount *= m_geometryCount;
  521. }
  522. if (m_separateClusterBlasForEachInstance)
  523. {
  524. for (int i{ 0 }; i < m_geometryCount; i++)
  525. {
  526. auto& subMesh{ data.m_rtSubMeshes.emplace_back() };
  527. subMesh.m_meshInfoHandle = GetMeshFeatureProcessor().AcquireMeshInfoEntry();
  528. subMesh.m_clusterBlasDescriptor = clusterDescriptor;
  529. subMesh.m_clusterBlasDescriptor->m_srcInfosArrayBufferView = m_srcInfosArrayBuffer->GetBufferView(
  530. RHI::BufferViewDescriptor::CreateStructured(
  531. i * clusterCountPerInstance, clusterCountPerInstance, sizeof(RHI::RayTracingClasBuildTriangleClusterInfo)));
  532. auto clusterOffsetBufferView{ m_clusterStreamOffsets->GetBufferView(
  533. RHI::BufferViewDescriptor::CreateRaw(
  534. i * clusterCountPerInstance * sizeof(RHI::RayTracingClasClusterOffsetInfo),
  535. clusterCountPerInstance * sizeof(RHI::RayTracingClasClusterOffsetInfo))) };
  536. updateMeshInfoBuffers(subMesh.m_meshInfoHandle, clusterOffsetBufferView);
  537. }
  538. }
  539. else
  540. {
  541. auto& subMesh{ data.m_rtSubMeshes.emplace_back() };
  542. subMesh.m_meshInfoHandle = GetMeshFeatureProcessor().AcquireMeshInfoEntry();
  543. subMesh.m_clusterBlasDescriptor = clusterDescriptor;
  544. subMesh.m_clusterBlasDescriptor->m_srcInfosArrayBufferView = m_srcInfosArrayBuffer->GetBufferView(
  545. RHI::BufferViewDescriptor::CreateStructured(0, totalClusterCount, sizeof(RHI::RayTracingClasBuildTriangleClusterInfo)));
  546. auto clusterOffsetBufferView{ m_clusterStreamOffsets->GetBufferView(
  547. RHI::BufferViewDescriptor::CreateRaw(0, totalClusterCount * sizeof(RHI::RayTracingClasClusterOffsetInfo))) };
  548. updateMeshInfoBuffers(subMesh.m_meshInfoHandle, clusterOffsetBufferView);
  549. }
  550. GetRayTracingFeatureProcessor().AddMesh(data.m_uuid, data.m_rtMesh, data.m_rtSubMeshes);
  551. }
  552. else
  553. {
  554. AZ_Assert(false, "Unsupported m_accelerationStructureType");
  555. }
  556. }
  557. void RayTracingVertexAnimationExampleComponent::AddVertexAnimationPass(AZ::RPI::RenderPipeline* renderPipeline)
  558. {
  559. Name passName{ "VertexAnimationPass" };
  560. auto filter{ AZ::RPI::PassFilter::CreateWithPassName(passName, renderPipeline) };
  561. if (RPI::PassSystemInterface::Get()->FindFirstPass(filter))
  562. {
  563. return;
  564. }
  565. auto pass{ RPI::PassSystemInterface::Get()->CreatePassFromTemplate(Name{ "VertexAnimationTemplate" }, passName) };
  566. if (!pass)
  567. {
  568. AZ_Assert(false, "Failed to create VertexAnimationPass");
  569. return;
  570. }
  571. m_vertexAnimationPass = AZStd::static_pointer_cast<Render::VertexAnimationPass>(pass);
  572. if (!m_vertexAnimationPass)
  573. {
  574. AZ_Assert(false, "VertexAnimationPass is not a ComputePass");
  575. return;
  576. }
  577. SetVertexAnimationPassData();
  578. renderPipeline->AddPassBefore(m_vertexAnimationPass, Name{ "RayTracingAccelerationStructurePass" });
  579. }
  580. void RayTracingVertexAnimationExampleComponent::SetVertexAnimationPassData()
  581. {
  582. m_vertexAnimationPass->SetTargetThreadCounts(m_vertexCountPerInstance * m_geometryCount, 1, 1);
  583. m_vertexAnimationPass->SetSourceBuffer(m_sourceGeometryBuffer);
  584. m_vertexAnimationPass->SetTargetBuffer(m_targetGeometryBuffer);
  585. m_vertexAnimationPass->SetInstanceOffsetBuffer(m_instanceOffsetDataBuffer);
  586. m_vertexAnimationPass->SetVertexCountPerInstance(m_vertexCountPerInstance);
  587. m_vertexAnimationPass->SetTargetVertexStridePerInstance(m_targetVertexStridePerInstance);
  588. m_vertexAnimationPass->SetInstanceCount(m_geometryCount);
  589. m_vertexAnimationPass->QueueForBuildAndInitialization();
  590. }
  591. void RayTracingVertexAnimationExampleComponent::DrawSidebar()
  592. {
  593. bool buildTypeUpdated{ false };
  594. if (m_imguiSidebar.Begin())
  595. {
  596. if (ImGui::Button("Start benchmark"))
  597. {
  598. m_currentPerformanceConfiguration = 0;
  599. }
  600. ImGui::Spacing();
  601. ImGui::Separator();
  602. if (ImGui::InputInt("Geometry count", &m_geometryCount, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue))
  603. {
  604. buildTypeUpdated = true;
  605. m_geometryCount = AZStd::max(m_geometryCount, 1);
  606. }
  607. ImGui::Text("RTAS Type:");
  608. buildTypeUpdated |=
  609. ImGuiHelper::RadioButton("Triangle BLAS", &m_accelerationStructureType, AccelerationStructureType::TriangleBLAS);
  610. buildTypeUpdated |=
  611. ImGuiHelper::RadioButton("CLAS + Cluster BLAS", &m_accelerationStructureType, AccelerationStructureType::CLAS_ClusterBLAS);
  612. ImGui::Spacing();
  613. ImGui::Text("RT Debug Type:");
  614. bool rayTracingDebugViewModeUpdated{ false };
  615. rayTracingDebugViewModeUpdated |=
  616. ImGuiHelper::RadioButton("Instance Index", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::InstanceIndex);
  617. rayTracingDebugViewModeUpdated |=
  618. ImGuiHelper::RadioButton("Instance ID", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::InstanceID);
  619. rayTracingDebugViewModeUpdated |=
  620. ImGuiHelper::RadioButton("Cluster ID", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::ClusterID);
  621. rayTracingDebugViewModeUpdated |=
  622. ImGuiHelper::RadioButton("Primitive Index", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::PrimitiveIndex);
  623. rayTracingDebugViewModeUpdated |= ImGuiHelper::RadioButton(
  624. "Barycentric Coordinates", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::Barycentrics);
  625. rayTracingDebugViewModeUpdated |=
  626. ImGuiHelper::RadioButton("Normals", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::Normals);
  627. rayTracingDebugViewModeUpdated |=
  628. ImGuiHelper::RadioButton("UV Coordinates", &m_rayTracingDebugViewMode, Render::RayTracingDebugViewMode::UVs);
  629. if (rayTracingDebugViewModeUpdated)
  630. {
  631. auto& rayTracingDebugFeatureProcessor{ GetRayTracingDebugFeatureProcessor() };
  632. rayTracingDebugFeatureProcessor.GetSettingsInterface()->SetDebugViewMode(m_rayTracingDebugViewMode);
  633. }
  634. ImGui::Spacing();
  635. ImGui::Separator();
  636. ImGui::Spacing();
  637. ImGui::Text("Performance:");
  638. // Cannot use m_imGuiFrameTimer.Tick since times in seconds are rounded to 0
  639. ImGui::Text(
  640. "Frame time: %.2f ms (%.0f fps)", m_imGuiFrameTimer.GetDisplayedAverage() * 1000.f,
  641. 1.f / m_imGuiFrameTimer.GetDisplayedAverage());
  642. ImGui::Text("RTAS build time: %.2f ms", m_accelerationStructurePassTimer.GetDisplayedAverage() * 1000.f);
  643. ImGui::Text("RT pass time: %.2f ms", m_rayTracingPassTimer.GetDisplayedAverage() * 1000.f);
  644. m_imguiSidebar.End();
  645. }
  646. if (buildTypeUpdated)
  647. {
  648. CreateRayTracingGeometry();
  649. }
  650. }
  651. void RayTracingVertexAnimationExampleComponent::PrintPerformanceResults()
  652. {
  653. AZStd::vector<AZStd::vector<AZStd::string>> table;
  654. table.push_back({ "Geometry", "BLAS", "CLAS", "BLAS", "CLAS", "BLAS", "CLAS" });
  655. table.push_back({ "count", "build", "build", "trace", "trace", "sum", "sum" });
  656. table.push_back({ "--------", "-----", "-----", "-----", "-----", "----", "----" });
  657. for (size_t i{ 0 }; i < m_performanceConfigurations.size(); i += 2)
  658. {
  659. const auto& blasResult{ m_performanceResults[i] };
  660. const auto& clasResult{ m_performanceResults[i + 1] };
  661. table.push_back(
  662. {
  663. AZStd::to_string(m_performanceConfigurations[i].m_geometryCount),
  664. AZStd::string::format("%.2f", blasResult.m_buildTime * 1000.f),
  665. AZStd::string::format("%.2f", clasResult.m_buildTime * 1000.f),
  666. AZStd::string::format("%.2f", blasResult.m_traceRayTime * 1000.f),
  667. AZStd::string::format("%.2f", clasResult.m_traceRayTime * 1000.f),
  668. AZStd::string::format("%.2f", (blasResult.m_buildTime + blasResult.m_traceRayTime) * 1000.f),
  669. AZStd::string::format("%.2f", (clasResult.m_buildTime + clasResult.m_traceRayTime) * 1000.f),
  670. });
  671. }
  672. AZStd::vector<int> columnWidths(table[0].size(), 0);
  673. for (size_t row{ 0 }; row < table.size(); row++)
  674. {
  675. for (size_t column{ 0 }; column < table[row].size(); column++)
  676. {
  677. columnWidths[column] = AZStd::max(columnWidths[column], aznumeric_cast<int>(table[row][column].size()));
  678. }
  679. }
  680. for (size_t row{ 0 }; row < table.size(); row++)
  681. {
  682. AZStd::string line;
  683. for (size_t column{ 0 }; column < table[row].size(); column++)
  684. {
  685. if (column != 0)
  686. {
  687. line += " | ";
  688. }
  689. int textWidth{ aznumeric_cast<int>(table[row][column].size()) };
  690. int cellWidth{ columnWidths[column] };
  691. int leftPad{ (cellWidth - textWidth) / 2 };
  692. int rightPad{ cellWidth - textWidth - leftPad };
  693. line += AZStd::string(leftPad, ' ');
  694. line += table[row][column];
  695. line += AZStd::string(rightPad, ' ');
  696. }
  697. AZ_Info("Performance", "%s\n", line.c_str());
  698. }
  699. }
  700. Render::MeshFeatureProcessorInterface& RayTracingVertexAnimationExampleComponent::GetMeshFeatureProcessor()
  701. {
  702. if (!m_meshFeatureProcessor)
  703. {
  704. RPI::Scene* scene{ RPI::Scene::GetSceneForEntityContextId(GetEntityContextId()) };
  705. auto featureProcessor{ scene->GetFeatureProcessor<Render::MeshFeatureProcessorInterface>() };
  706. AZ_Assert(featureProcessor != nullptr, "MeshFeatureProcessor not found");
  707. m_meshFeatureProcessor = featureProcessor;
  708. }
  709. return *m_meshFeatureProcessor;
  710. }
  711. Render::RayTracingFeatureProcessorInterface& RayTracingVertexAnimationExampleComponent::GetRayTracingFeatureProcessor()
  712. {
  713. if (!m_rayTracingFeatureProcessor)
  714. {
  715. RPI::Scene* scene{ RPI::Scene::GetSceneForEntityContextId(GetEntityContextId()) };
  716. auto featureProcessor{ scene->GetFeatureProcessor<Render::RayTracingFeatureProcessorInterface>() };
  717. AZ_Assert(featureProcessor != nullptr, "RayTracingFeatureProcessor not found");
  718. m_rayTracingFeatureProcessor = featureProcessor;
  719. }
  720. return *m_rayTracingFeatureProcessor;
  721. }
  722. Render::RayTracingDebugFeatureProcessorInterface& RayTracingVertexAnimationExampleComponent::GetRayTracingDebugFeatureProcessor()
  723. {
  724. if (!m_rayTracingDebugFeatureProcessor)
  725. {
  726. RPI::Scene* scene{ RPI::Scene::GetSceneForEntityContextId(GetEntityContextId()) };
  727. auto featureProcessor{ scene->GetFeatureProcessor<Render::RayTracingDebugFeatureProcessorInterface>() };
  728. AZ_Assert(featureProcessor != nullptr, "RayTracingDebugFeatureProcessor not found");
  729. m_rayTracingDebugFeatureProcessor = featureProcessor;
  730. }
  731. return *m_rayTracingDebugFeatureProcessor;
  732. }
  733. } // namespace AtomSampleViewer