QueryExampleComponent.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  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 <Atom/RHI/CommandList.h>
  9. #include <Atom/RPI.Public/Shader/Shader.h>
  10. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  11. #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  12. #include <Atom/RHI.Reflect/QueryPoolDescriptor.h>
  13. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <RHI/QueryExampleComponent.h>
  16. #include <SampleComponentManager.h>
  17. #include <SampleComponentConfig.h>
  18. #include <Utils/Utils.h>
  19. namespace AtomSampleViewer
  20. {
  21. namespace QueryExample
  22. {
  23. const char* ShaderFilePath = "Shaders/RHI/colorMesh.azshader";
  24. const char* SampleName = "QueryExample";
  25. const char* PredicationBufferId = "bufferAttachmentId";
  26. }
  27. void QueryExampleComponent::Reflect(AZ::ReflectContext* context)
  28. {
  29. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  30. {
  31. serializeContext->Class<QueryExampleComponent, AZ::Component>()
  32. ->Version(0)
  33. ;
  34. }
  35. }
  36. QueryExampleComponent::QueryExampleComponent()
  37. {
  38. m_supportRHISamplePipeline = true;
  39. }
  40. void QueryExampleComponent::Activate()
  41. {
  42. CreateGeometryResources();
  43. CreateShaderResources();
  44. CreateCopyBufferScopeProducer();
  45. CreateScopeProducer();
  46. CreateQueryResources();
  47. CreatePredicationResources();
  48. SetQueryType(m_currentType);
  49. m_imguiSidebar.Activate();
  50. AZ::TickBus::Handler::BusConnect();
  51. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  52. }
  53. void QueryExampleComponent::Deactivate()
  54. {
  55. m_quadInputAssemblyBuffer = nullptr;
  56. m_inputAssemblyBufferPool = nullptr;
  57. m_quadPipelineState = nullptr;
  58. m_boudingBoxPipelineState = nullptr;
  59. m_shaderResourceGroups.fill(nullptr);
  60. QueryEntry nullQueryEntry;
  61. nullQueryEntry.m_isValid = false;
  62. nullQueryEntry.m_query = nullptr;
  63. m_occlusionQueries.fill(nullQueryEntry);
  64. m_timestampQueries.fill(nullQueryEntry);
  65. m_statisticsQueries.fill(nullQueryEntry);
  66. m_occlusionQueryPool = nullptr;
  67. m_timeStampQueryPool = nullptr;
  68. m_statisticsQueryPool = nullptr;
  69. m_predicationBuffer = nullptr;
  70. m_predicationBufferPool = nullptr;
  71. m_timestampEnabled = false;
  72. m_pipelineStatisticsEnabled = false;
  73. m_precisionOcclusionEnabled = false;
  74. m_imguiSidebar.Deactivate();
  75. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  76. AZ::TickBus::Handler::BusDisconnect();
  77. m_windowContext = nullptr;
  78. m_scopeProducers.clear();
  79. }
  80. void QueryExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  81. {
  82. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  83. }
  84. void QueryExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  85. {
  86. m_elapsedTime += deltaTime;
  87. switch (m_currentType)
  88. {
  89. case QueryType::Occlusion:
  90. m_currentOcclusionQueryIndex = (m_currentOcclusionQueryIndex + 1) % static_cast<uint32_t>(m_occlusionQueries.size());
  91. break;
  92. case QueryType::Predication:
  93. // For predication we use only one query
  94. m_currentOcclusionQueryIndex = 0;
  95. break;
  96. }
  97. {
  98. auto scaleMatrix = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.2, 0.2, 0.2));
  99. [[maybe_unused]] bool success = m_shaderResourceGroups[0]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(AZ::Vector3(0, 0, 0.5)));
  100. success &= m_shaderResourceGroups[0]->SetConstant(m_colorConstantIndex, AZ::Vector4(1.0, 0, 0, 1.0));
  101. AZ_Warning(QueryExample::SampleName, success, "Failed to set SRG Constant data");
  102. }
  103. {
  104. auto scaleMatrix = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.5, 0.5, 0.5));
  105. AZ::Vector3 translation(
  106. sinf(m_elapsedTime),
  107. 0.0f,
  108. 0.0f);
  109. [[maybe_unused]] bool success = m_shaderResourceGroups[1]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(translation));
  110. success &= m_shaderResourceGroups[1]->SetConstant(m_colorConstantIndex, AZ::Color(1.f, 1.f, 1.f, 0.5f).GetAsVector4());
  111. AZ_Warning(QueryExample::SampleName, success, "Failed to set SRG Constant data");
  112. }
  113. {
  114. auto scaleMatrix = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.2, 0.2, 0.2));
  115. [[maybe_unused]] bool success = m_shaderResourceGroups[2]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(AZ::Vector3(0, 0, 0.49)));
  116. AZ_Warning(QueryExample::SampleName, success, "Failed to set SRG Constant data");
  117. }
  118. if(m_timestampEnabled)
  119. {
  120. // Timestamp use two queries to get the time.
  121. m_currentTimestampQueryIndex = (m_currentTimestampQueryIndex + 2) % m_timestampQueries.size();
  122. }
  123. if (m_pipelineStatisticsEnabled)
  124. {
  125. m_currentStatisticsQueryIndex = (m_currentStatisticsQueryIndex + 1) % static_cast<uint32_t>(m_statisticsQueries.size());
  126. }
  127. if (m_imguiSidebar.Begin())
  128. {
  129. DrawSampleSettings();
  130. }
  131. }
  132. void QueryExampleComponent::CreateGeometryResources()
  133. {
  134. using namespace AZ;
  135. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  136. m_inputAssemblyBufferPool = aznew RHI::BufferPool();
  137. RHI::BufferPoolDescriptor bufferPoolDesc;
  138. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  139. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  140. m_inputAssemblyBufferPool->Init(bufferPoolDesc);
  141. {
  142. // Create quad buffer and views
  143. BufferData bufferData;
  144. SetFullScreenRect(bufferData.m_positions.data(), nullptr, bufferData.m_indices.data());
  145. m_quadInputAssemblyBuffer = aznew RHI::Buffer();
  146. RHI::ResultCode result = RHI::ResultCode::Success;
  147. RHI::BufferInitRequest request;
  148. request.m_buffer = m_quadInputAssemblyBuffer.get();
  149. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
  150. request.m_initialData = &bufferData;
  151. result = m_inputAssemblyBufferPool->InitBuffer(request);
  152. if (result != RHI::ResultCode::Success)
  153. {
  154. AZ_Error(QueryExample::SampleName, false, "Failed to initialize position buffer with error code %d", result);
  155. return;
  156. }
  157. m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
  158. m_geometryView.SetIndexBufferView({
  159. *m_quadInputAssemblyBuffer,
  160. offsetof(BufferData, m_indices),
  161. sizeof(BufferData::m_indices),
  162. RHI::IndexFormat::Uint16
  163. });
  164. m_geometryView.AddStreamBufferView({
  165. *m_quadInputAssemblyBuffer,
  166. offsetof(BufferData, m_positions),
  167. sizeof(BufferData::m_positions),
  168. sizeof(VertexPosition)
  169. });
  170. RHI::InputStreamLayoutBuilder layoutBuilder;
  171. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  172. m_quadInputStreamLayout = layoutBuilder.End();
  173. RHI::ValidateStreamBufferViews(m_quadInputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
  174. }
  175. }
  176. const AZ::RHI::PipelineState* CreatePipelineState(
  177. const AZ::RHI::InputStreamLayout& inputStreamLayout,
  178. const AZ::Data::Instance<AZ::RPI::Shader>& shader,
  179. const AZ::RHI::Format format,
  180. const AZ::RHI::TargetBlendState& blendState)
  181. {
  182. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  183. pipelineStateDescriptor.m_inputStreamLayout = inputStreamLayout;
  184. auto shaderVariant = shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
  185. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  186. AZ::RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  187. attachmentsBuilder.AddSubpass()
  188. ->RenderTargetAttachment(format)
  189. ->DepthStencilAttachment(AZ::RHI::Format::D32_FLOAT);
  190. [[maybe_unused]] AZ::RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  191. AZ_Assert(result == AZ::RHI::ResultCode::Success, "Failed to create render attachment layout");
  192. pipelineStateDescriptor.m_renderStates.m_blendState.m_targets[0] = blendState;
  193. pipelineStateDescriptor.m_renderStates.m_depthStencilState = AZ::RHI::DepthStencilState::CreateDepth();
  194. return shader->AcquirePipelineState(pipelineStateDescriptor);
  195. }
  196. void QueryExampleComponent::CreateShaderResources()
  197. {
  198. using namespace AZ;
  199. auto shader = LoadShader(QueryExample::ShaderFilePath, QueryExample::SampleName);
  200. if (!shader)
  201. {
  202. return;
  203. }
  204. for (size_t i = 0; i < m_shaderResourceGroups.size(); ++i)
  205. {
  206. m_shaderResourceGroups[i] = CreateShaderResourceGroup(shader, "InstanceSrg", QueryExample::SampleName);
  207. }
  208. const Name objectMatrixConstantId{ "m_objectMatrix" };
  209. FindShaderInputIndex(&m_objectMatrixConstantIndex, m_shaderResourceGroups.front(), objectMatrixConstantId, QueryExample::SampleName);
  210. const Name colorConstantId{ "m_color" };
  211. FindShaderInputIndex(&m_colorConstantIndex, m_shaderResourceGroups.front(), colorConstantId, QueryExample::SampleName);
  212. RHI::TargetBlendState blendState;
  213. blendState.m_enable = true;
  214. blendState.m_blendSource = RHI::BlendFactor::AlphaSource;
  215. blendState.m_blendDest = RHI::BlendFactor::AlphaSourceInverse;
  216. // The top quad is translucent to see what's behind and if the back quad is being rendered.
  217. m_quadPipelineState = CreatePipelineState(m_quadInputStreamLayout, shader, m_outputFormat, blendState);
  218. if (!m_quadPipelineState)
  219. {
  220. AZ_Error(QueryExample::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", QueryExample::ShaderFilePath);
  221. return;
  222. }
  223. // Bounding box quad doesn't write to the render targets.
  224. blendState.m_writeMask = 0;
  225. m_boudingBoxPipelineState = CreatePipelineState(m_quadInputStreamLayout, shader, m_outputFormat, blendState);
  226. if (!m_boudingBoxPipelineState)
  227. {
  228. AZ_Error(QueryExample::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", QueryExample::ShaderFilePath);
  229. return;
  230. }
  231. }
  232. void QueryExampleComponent::CreateCopyBufferScopeProducer()
  233. {
  234. // This scope is used for copying the results from the query to a buffer that is later
  235. // used for predication.
  236. using namespace AZ;
  237. struct ScopeData
  238. {
  239. };
  240. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  241. {
  242. // Handle the case where the window may have closed
  243. if (m_currentType != QueryType::Predication)
  244. {
  245. return;
  246. }
  247. [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(RHI::AttachmentId{QueryExample::PredicationBufferId}, m_predicationBuffer);
  248. AZ_Error(QueryExample::SampleName, result == RHI::ResultCode::Success, "Failed to import predication buffer with error %d", result);
  249. frameGraph.UseQueryPool(
  250. m_occlusionQueryPool,
  251. RHI::Interval(m_currentOcclusionQueryIndex, m_currentOcclusionQueryIndex),
  252. RHI::QueryPoolScopeAttachmentType::Local,
  253. RHI::ScopeAttachmentAccess::Read);
  254. frameGraph.UseCopyAttachment(m_predicationBufferAttachmentDescriptor, RHI::ScopeAttachmentAccess::Write);
  255. frameGraph.SetEstimatedItemCount(1);
  256. };
  257. AZ::RHI::EmptyCompileFunction<ScopeData> compileFunction;
  258. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  259. {
  260. // Handle the case where the window may have closed
  261. if (m_currentType != QueryType::Predication)
  262. {
  263. return;
  264. }
  265. RHI::DeviceCopyQueryToBufferDescriptor descriptor;
  266. descriptor.m_sourceQueryPool = m_occlusionQueryPool->GetDeviceQueryPool(context.GetDeviceIndex()).get();
  267. descriptor.m_firstQuery = m_occlusionQueries[m_currentOcclusionQueryIndex].m_query->GetHandle(context.GetDeviceIndex());
  268. descriptor.m_queryCount = 1;
  269. descriptor.m_destinationBuffer = m_predicationBuffer->GetDeviceBuffer(context.GetDeviceIndex()).get();
  270. descriptor.m_destinationOffset = 0;
  271. descriptor.m_destinationStride = sizeof(uint64_t);
  272. RHI::DeviceCopyItem copyItem(descriptor);
  273. context.GetCommandList()->Submit(copyItem);
  274. };
  275. const AZ::RHI::ScopeId occlusionScope("CopyPredicationBuffer");
  276. m_scopeProducers.emplace_back(
  277. aznew RHI::ScopeProducerFunction<
  278. ScopeData,
  279. decltype(prepareFunction),
  280. decltype(compileFunction),
  281. decltype(executeFunction)>(
  282. occlusionScope,
  283. ScopeData{},
  284. prepareFunction,
  285. compileFunction,
  286. executeFunction));
  287. }
  288. void QueryExampleComponent::CreateScopeProducer()
  289. {
  290. using namespace AZ;
  291. struct ScopeData
  292. {
  293. };
  294. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  295. {
  296. {
  297. // Binds the swapchain color attachment.
  298. RHI::ImageScopeAttachmentDescriptor descriptor;
  299. descriptor.m_attachmentId = m_outputAttachmentId;
  300. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  301. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  302. frameGraph.UseColorAttachment(descriptor);
  303. }
  304. {
  305. // Binds the transient depth attachment
  306. auto depthAttachmentId = AZ::RHI::AttachmentId{ "DepthStencilID" };
  307. const AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
  308. AZ::RHI::ImageBindFlags::DepthStencil,
  309. static_cast<uint32_t>(GetViewportWidth()),
  310. static_cast<uint32_t>(GetViewportHeight()),
  311. AZ::RHI::Format::D32_FLOAT);
  312. const AZ::RHI::TransientImageDescriptor transientImageDescriptor(depthAttachmentId, imageDescriptor);
  313. frameGraph.GetAttachmentDatabase().CreateTransientImage(transientImageDescriptor);
  314. AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
  315. dsDesc.m_attachmentId = depthAttachmentId;
  316. dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
  317. dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
  318. dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
  319. frameGraph.UseDepthStencilAttachment(
  320. dsDesc, AZ::RHI::ScopeAttachmentAccess::Write,
  321. AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
  322. }
  323. // Query pools
  324. {
  325. frameGraph.UseQueryPool(
  326. m_occlusionQueryPool,
  327. RHI::Interval(m_currentOcclusionQueryIndex, m_currentOcclusionQueryIndex),
  328. m_currentType == QueryType::Predication ? RHI::QueryPoolScopeAttachmentType::Local : RHI::QueryPoolScopeAttachmentType::Global,
  329. RHI::ScopeAttachmentAccess::Write);
  330. if (m_timestampEnabled)
  331. {
  332. frameGraph.UseQueryPool(
  333. m_timeStampQueryPool,
  334. RHI::Interval(m_currentTimestampQueryIndex, m_currentTimestampQueryIndex + 1),
  335. RHI::QueryPoolScopeAttachmentType::Global,
  336. RHI::ScopeAttachmentAccess::Write);
  337. }
  338. if (m_pipelineStatisticsEnabled)
  339. {
  340. frameGraph.UseQueryPool(
  341. m_statisticsQueryPool,
  342. RHI::Interval(m_currentStatisticsQueryIndex, m_currentStatisticsQueryIndex),
  343. RHI::QueryPoolScopeAttachmentType::Global,
  344. RHI::ScopeAttachmentAccess::Write);
  345. }
  346. }
  347. // Add the predication buffer if necessary
  348. if (m_currentType == QueryType::Predication)
  349. {
  350. frameGraph.UseAttachment(
  351. m_predicationBufferAttachmentDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Predication,
  352. RHI::ScopeAttachmentStage::Predication);
  353. }
  354. // We will submit a single draw item.
  355. frameGraph.SetEstimatedItemCount(3);
  356. };
  357. using namespace AZ;
  358. const auto compileFunction = [this]([[maybe_unused]] const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  359. {
  360. for (auto& srg : m_shaderResourceGroups)
  361. {
  362. srg->Compile();
  363. }
  364. };
  365. const auto executeFunction = [this]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  366. {
  367. RHI::CommandList* commandList = context.GetCommandList();
  368. // Set persistent viewport and scissor state.
  369. commandList->SetViewports(&m_viewport, 1);
  370. commandList->SetScissors(&m_scissor, 1);
  371. {
  372. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  373. m_shaderResourceGroups[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  374. };
  375. RHI::DeviceDrawItem drawItem;
  376. drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
  377. drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
  378. drawItem.m_pipelineState = m_quadPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  379. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  380. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  381. // Draw possibly occluded quad
  382. {
  383. if (m_timestampEnabled)
  384. {
  385. m_timestampQueries[m_currentTimestampQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->WriteTimestamp(*commandList);
  386. m_timestampQueries[m_currentTimestampQueryIndex].m_isValid = true;
  387. }
  388. if (m_pipelineStatisticsEnabled)
  389. {
  390. m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->Begin(*commandList);
  391. }
  392. switch (m_currentType)
  393. {
  394. case QueryType::Occlusion:
  395. {
  396. // This is the oldest occlusion query (3 frames ago)
  397. uint64_t occlusionResults = 0;
  398. auto& queryEntry = m_occlusionQueries[(m_currentOcclusionQueryIndex + 1) % m_occlusionQueries.size()];
  399. if (queryEntry.m_isValid)
  400. {
  401. m_occlusionQueryPool->GetResults(queryEntry.m_query.get(), &occlusionResults, sizeof(occlusionResults), RHI::QueryResultFlagBits::Wait);
  402. }
  403. if (occlusionResults)
  404. {
  405. commandList->Submit(drawItem, 0);
  406. }
  407. break;
  408. }
  409. case QueryType::Predication:
  410. {
  411. commandList->BeginPredication(*m_predicationBuffer->GetDeviceBuffer(context.GetDeviceIndex()), 0, RHI::PredicationOp::EqualZero);
  412. commandList->Submit(drawItem, 0);
  413. commandList->EndPredication();
  414. break;
  415. }
  416. }
  417. if (m_pipelineStatisticsEnabled)
  418. {
  419. m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->End(*commandList);
  420. m_statisticsQueries[m_currentStatisticsQueryIndex].m_isValid = true;
  421. }
  422. if (m_timestampEnabled)
  423. {
  424. m_timestampQueries[m_currentTimestampQueryIndex + 1].m_query->GetDeviceQuery(context.GetDeviceIndex())->WriteTimestamp(*commandList);
  425. m_timestampQueries[m_currentTimestampQueryIndex + 1].m_isValid = true;
  426. }
  427. }
  428. // Draw occluding quad
  429. shaderResourceGroups[0] = m_shaderResourceGroups[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  430. commandList->Submit(drawItem, 1);
  431. // Draw quad to use for the oclussion query
  432. drawItem.m_pipelineState = m_boudingBoxPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  433. shaderResourceGroups[0] = m_shaderResourceGroups[2]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  434. auto& queryEntry = m_occlusionQueries[m_currentOcclusionQueryIndex];
  435. queryEntry.m_query->GetDeviceQuery(context.GetDeviceIndex())->Begin(*commandList, m_precisionOcclusionEnabled ? RHI::QueryControlFlags::PreciseOcclusion : RHI::QueryControlFlags::None);
  436. commandList->Submit(drawItem, 2);
  437. queryEntry.m_query->GetDeviceQuery(context.GetDeviceIndex())->End(*commandList);
  438. queryEntry.m_isValid = true;
  439. }
  440. };
  441. const AZ::RHI::ScopeId OcclusionScope("Occlusion");
  442. m_scopeProducers.emplace_back(
  443. aznew RHI::ScopeProducerFunction<
  444. ScopeData,
  445. decltype(prepareFunction),
  446. decltype(compileFunction),
  447. decltype(executeFunction)>(
  448. OcclusionScope,
  449. ScopeData{},
  450. prepareFunction,
  451. compileFunction,
  452. executeFunction));
  453. }
  454. template<class T>
  455. void CreateQueries(AZ::RHI::Ptr<AZ::RHI::QueryPool>& queryPool, T& queries, AZ::RHI::QueryType type, AZ::RHI::PipelineStatisticsFlags statisticsMask = AZ::RHI::PipelineStatisticsFlags::None)
  456. {
  457. using namespace AZ;
  458. RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  459. const auto& features = device->GetFeatures();
  460. if (!RHI::CheckBitsAll(
  461. features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Graphics)],
  462. static_cast<RHI::QueryTypeFlags>(AZ_BIT(static_cast<uint32_t>(type)))))
  463. {
  464. return;
  465. }
  466. RHI::QueryPoolDescriptor queryPoolDesc;
  467. queryPoolDesc.m_queriesCount = static_cast<uint32_t>(queries.size());
  468. queryPoolDesc.m_type = type;
  469. queryPoolDesc.m_pipelineStatisticsMask = statisticsMask;
  470. queryPool = aznew RHI::QueryPool;
  471. auto result = queryPool->Init(queryPoolDesc);
  472. if (result != RHI::ResultCode::Success)
  473. {
  474. AZ_Assert(false, "Failed to createa query pool");
  475. return;
  476. }
  477. for (auto& queryEntry : queries)
  478. {
  479. queryEntry.m_query = aznew RHI::Query;
  480. queryPool->InitQuery(queryEntry.m_query.get());
  481. }
  482. }
  483. void QueryExampleComponent::CreateQueryResources()
  484. {
  485. CreateQueries(m_occlusionQueryPool, m_occlusionQueries, AZ::RHI::QueryType::Occlusion);
  486. CreateQueries(m_timeStampQueryPool, m_timestampQueries, AZ::RHI::QueryType::Timestamp);
  487. AZ::RHI::PipelineStatisticsFlags statisticsMask =
  488. AZ::RHI::PipelineStatisticsFlags::IAVertices | AZ::RHI::PipelineStatisticsFlags::VSInvocations |
  489. AZ::RHI::PipelineStatisticsFlags::IAPrimitives | AZ::RHI::PipelineStatisticsFlags::CInvocations |
  490. AZ::RHI::PipelineStatisticsFlags::CPrimitives | AZ::RHI::PipelineStatisticsFlags::PSInvocations;
  491. CreateQueries(m_statisticsQueryPool, m_statisticsQueries, AZ::RHI::QueryType::PipelineStatistics, statisticsMask);
  492. }
  493. void QueryExampleComponent::CreatePredicationResources()
  494. {
  495. using namespace AZ;
  496. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  497. if (!device->GetFeatures().m_predication)
  498. {
  499. return;
  500. }
  501. m_predicationBufferPool = aznew RHI::BufferPool();
  502. RHI::BufferPoolDescriptor bufferPoolDesc;
  503. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::CopyWrite;
  504. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  505. m_predicationBufferPool->Init(bufferPoolDesc);
  506. m_predicationBuffer = aznew RHI::Buffer();
  507. RHI::BufferInitRequest request;
  508. request.m_buffer = m_predicationBuffer.get();
  509. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::CopyWrite, sizeof(uint64_t) };
  510. RHI::ResultCode result = m_predicationBufferPool->InitBuffer(request);
  511. if (result != RHI::ResultCode::Success)
  512. {
  513. AZ_Error(QueryExample::SampleName, false, "Failed to initialize position buffer with error code %d", result);
  514. return;
  515. }
  516. m_predicationBufferAttachmentDescriptor.m_attachmentId = QueryExample::PredicationBufferId;
  517. const uint64_t resultsCount = 1;
  518. m_predicationBufferAttachmentDescriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  519. 0,
  520. resultsCount,
  521. static_cast<uint32_t>(m_predicationBuffer->GetDescriptor().m_byteCount / resultsCount));
  522. }
  523. void QueryExampleComponent::SetQueryType(QueryType type)
  524. {
  525. if (type == m_currentType)
  526. {
  527. return;
  528. }
  529. m_currentType = type;
  530. m_elapsedTime = 0;
  531. if (m_currentType == QueryType::Predication)
  532. {
  533. m_precisionOcclusionEnabled = false;
  534. }
  535. }
  536. void QueryExampleComponent::DrawSampleSettings()
  537. {
  538. const auto& features = Utils::GetRHIDevice()->GetFeatures();
  539. ImGui::Spacing();
  540. AZStd::vector<const char*> items = { "Occlusion" };
  541. if (features.m_predication)
  542. {
  543. items.push_back("Predication");
  544. }
  545. int current_item = static_cast<int>(m_currentType);
  546. ImGui::Text("Type");
  547. ScriptableImGui::Combo("", &current_item, items.data(), static_cast<int>(items.size()));
  548. SetQueryType(static_cast<QueryType>(current_item));
  549. if (m_currentType == QueryType::Occlusion && features.m_occlusionQueryPrecise)
  550. {
  551. // Precision occlusion is only available for the Occlusion query type
  552. ScriptableImGui::Checkbox("Enable Precision occlusion", &m_precisionOcclusionEnabled);
  553. }
  554. auto& supportedQueries = features.m_queryTypesMask[static_cast<uint32_t>(AZ::RHI::HardwareQueueClass::Graphics)];
  555. if (AZ::RHI::CheckBitsAll(supportedQueries, AZ::RHI::QueryTypeFlags::Timestamp))
  556. {
  557. ScriptableImGui::Checkbox("Enable Timestamp queries", &m_timestampEnabled);
  558. }
  559. if (AZ::RHI::CheckBitsAll(supportedQueries, AZ::RHI::QueryTypeFlags::PipelineStatistics))
  560. {
  561. ScriptableImGui::Checkbox("Enable Statistics queries", &m_pipelineStatisticsEnabled);
  562. }
  563. if (m_precisionOcclusionEnabled)
  564. {
  565. uint64_t occlusionResults = 0;
  566. // This is the oldest occlusion query (3 frames ago).
  567. auto queryEntry = m_occlusionQueries[(m_currentOcclusionQueryIndex + 1) % m_occlusionQueries.size()];
  568. if (queryEntry.m_isValid)
  569. {
  570. m_occlusionQueryPool->GetResults(queryEntry.m_query.get(), &occlusionResults, sizeof(occlusionResults), AZ::RHI::QueryResultFlagBits::Wait);
  571. }
  572. ImGui::Text("Occlusion Result: %llu", static_cast<unsigned long long>(occlusionResults));
  573. }
  574. if(m_timestampEnabled)
  575. {
  576. // This is the oldest pair of timestamp queries (3 frames ago).
  577. size_t beginIndex = (m_currentTimestampQueryIndex + 2) % m_timestampQueries.size();
  578. uint64_t timestamps[2] = {};
  579. if (m_timestampQueries[beginIndex].m_isValid && m_timestampQueries[beginIndex + 1].m_isValid)
  580. {
  581. AZ::RHI::Query* queries[] = { m_timestampQueries[beginIndex].m_query.get() , m_timestampQueries[beginIndex + 1].m_query.get() };
  582. m_timeStampQueryPool->GetResults(queries, 2, timestamps, 2, AZ::RHI::QueryResultFlagBits::Wait);
  583. }
  584. if (ImGui::CollapsingHeader("Timestamp", ImGuiTreeNodeFlags_DefaultOpen))
  585. {
  586. AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  587. auto durationInMicroseconds = device->GpuTimestampToMicroseconds(timestamps[1] - timestamps[0], AZ::RHI::HardwareQueueClass::Graphics);
  588. ImGui::Indent();
  589. ImGui::Text("Draw time: %llu us", static_cast<unsigned long long>(durationInMicroseconds.count()));
  590. ImGui::Unindent();
  591. }
  592. }
  593. if (m_pipelineStatisticsEnabled)
  594. {
  595. AZStd::vector<uint64_t> statistics;
  596. uint32_t statisticsCount = AZ::RHI::CountBitsSet(static_cast<uint64_t>(m_statisticsQueryPool->GetDescriptor().m_pipelineStatisticsMask));
  597. statistics.resize(statisticsCount);
  598. size_t queryFrameIndex = (m_currentStatisticsQueryIndex + 1) % m_statisticsQueries.size();
  599. if (m_statisticsQueries[queryFrameIndex].m_isValid)
  600. {
  601. m_statisticsQueryPool->GetResults(m_statisticsQueries[queryFrameIndex].m_query.get(), statistics.data(), statisticsCount, AZ::RHI::QueryResultFlagBits::Wait);
  602. }
  603. const char* statisticsName[] =
  604. {
  605. "IAVertices",
  606. "IAPrimitives",
  607. "VSInvocations",
  608. "CInvocations",
  609. "CPrimitives",
  610. "PSInvocations"
  611. };
  612. if (ImGui::CollapsingHeader("Statistics", ImGuiTreeNodeFlags_DefaultOpen))
  613. {
  614. ImGui::Indent();
  615. for (size_t i = 0; i < statistics.size(); ++i)
  616. {
  617. ImGui::Text("%s: %llu\n", statisticsName[i], static_cast<unsigned long long>(statistics[i]));
  618. }
  619. ImGui::Unindent();
  620. }
  621. }
  622. m_imguiSidebar.End();
  623. }
  624. } // namespace AtomSampleViewer