QueryExampleComponent.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  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_quadStreamBufferView = {
  158. *m_quadInputAssemblyBuffer,
  159. offsetof(BufferData, m_positions),
  160. sizeof(BufferData::m_positions),
  161. sizeof(VertexPosition)
  162. };
  163. RHI::InputStreamLayoutBuilder layoutBuilder;
  164. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  165. m_quadInputStreamLayout = layoutBuilder.End();
  166. RHI::ValidateStreamBufferViews(m_quadInputStreamLayout, AZStd::span<const RHI::StreamBufferView>(&m_quadStreamBufferView, 1));
  167. }
  168. }
  169. const AZ::RHI::PipelineState* CreatePipelineState(
  170. const AZ::RHI::InputStreamLayout& inputStreamLayout,
  171. const AZ::Data::Instance<AZ::RPI::Shader>& shader,
  172. const AZ::RHI::Format format,
  173. const AZ::RHI::TargetBlendState& blendState)
  174. {
  175. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  176. pipelineStateDescriptor.m_inputStreamLayout = inputStreamLayout;
  177. auto shaderVariant = shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
  178. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  179. AZ::RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  180. attachmentsBuilder.AddSubpass()
  181. ->RenderTargetAttachment(format)
  182. ->DepthStencilAttachment(AZ::RHI::Format::D32_FLOAT);
  183. [[maybe_unused]] AZ::RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  184. AZ_Assert(result == AZ::RHI::ResultCode::Success, "Failed to create render attachment layout");
  185. pipelineStateDescriptor.m_renderStates.m_blendState.m_targets[0] = blendState;
  186. pipelineStateDescriptor.m_renderStates.m_depthStencilState = AZ::RHI::DepthStencilState::CreateDepth();
  187. return shader->AcquirePipelineState(pipelineStateDescriptor);
  188. }
  189. void QueryExampleComponent::CreateShaderResources()
  190. {
  191. using namespace AZ;
  192. auto shader = LoadShader(QueryExample::ShaderFilePath, QueryExample::SampleName);
  193. if (!shader)
  194. {
  195. return;
  196. }
  197. for (size_t i = 0; i < m_shaderResourceGroups.size(); ++i)
  198. {
  199. m_shaderResourceGroups[i] = CreateShaderResourceGroup(shader, "InstanceSrg", QueryExample::SampleName);
  200. }
  201. const Name objectMatrixConstantId{ "m_objectMatrix" };
  202. FindShaderInputIndex(&m_objectMatrixConstantIndex, m_shaderResourceGroups.front(), objectMatrixConstantId, QueryExample::SampleName);
  203. const Name colorConstantId{ "m_color" };
  204. FindShaderInputIndex(&m_colorConstantIndex, m_shaderResourceGroups.front(), colorConstantId, QueryExample::SampleName);
  205. RHI::TargetBlendState blendState;
  206. blendState.m_enable = true;
  207. blendState.m_blendSource = RHI::BlendFactor::AlphaSource;
  208. blendState.m_blendDest = RHI::BlendFactor::AlphaSourceInverse;
  209. // The top quad is translucent to see what's behind and if the back quad is being rendered.
  210. m_quadPipelineState = CreatePipelineState(m_quadInputStreamLayout, shader, m_outputFormat, blendState);
  211. if (!m_quadPipelineState)
  212. {
  213. AZ_Error(QueryExample::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", QueryExample::ShaderFilePath);
  214. return;
  215. }
  216. // Bounding box quad doesn't write to the render targets.
  217. blendState.m_writeMask = 0;
  218. m_boudingBoxPipelineState = CreatePipelineState(m_quadInputStreamLayout, shader, m_outputFormat, blendState);
  219. if (!m_boudingBoxPipelineState)
  220. {
  221. AZ_Error(QueryExample::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", QueryExample::ShaderFilePath);
  222. return;
  223. }
  224. }
  225. void QueryExampleComponent::CreateCopyBufferScopeProducer()
  226. {
  227. // This scope is used for copying the results from the query to a buffer that is later
  228. // used for predication.
  229. using namespace AZ;
  230. struct ScopeData
  231. {
  232. };
  233. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  234. {
  235. // Handle the case where the window may have closed
  236. if (m_currentType != QueryType::Predication)
  237. {
  238. return;
  239. }
  240. [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(RHI::AttachmentId{QueryExample::PredicationBufferId}, m_predicationBuffer);
  241. AZ_Error(QueryExample::SampleName, result == RHI::ResultCode::Success, "Failed to import predication buffer with error %d", result);
  242. frameGraph.UseQueryPool(
  243. m_occlusionQueryPool,
  244. RHI::Interval(m_currentOcclusionQueryIndex, m_currentOcclusionQueryIndex),
  245. RHI::QueryPoolScopeAttachmentType::Local,
  246. RHI::ScopeAttachmentAccess::Read);
  247. frameGraph.UseCopyAttachment(m_predicationBufferAttachmentDescriptor, RHI::ScopeAttachmentAccess::Write);
  248. frameGraph.SetEstimatedItemCount(1);
  249. };
  250. AZ::RHI::EmptyCompileFunction<ScopeData> compileFunction;
  251. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  252. {
  253. // Handle the case where the window may have closed
  254. if (m_currentType != QueryType::Predication)
  255. {
  256. return;
  257. }
  258. RHI::DeviceCopyQueryToBufferDescriptor descriptor;
  259. descriptor.m_sourceQueryPool = m_occlusionQueryPool->GetDeviceQueryPool(context.GetDeviceIndex()).get();
  260. descriptor.m_firstQuery = m_occlusionQueries[m_currentOcclusionQueryIndex].m_query->GetHandle(context.GetDeviceIndex());
  261. descriptor.m_queryCount = 1;
  262. descriptor.m_destinationBuffer = m_predicationBuffer->GetDeviceBuffer(context.GetDeviceIndex()).get();
  263. descriptor.m_destinationOffset = 0;
  264. descriptor.m_destinationStride = sizeof(uint64_t);
  265. RHI::DeviceCopyItem copyItem(descriptor);
  266. context.GetCommandList()->Submit(copyItem);
  267. };
  268. const AZ::RHI::ScopeId occlusionScope("CopyPredicationBuffer");
  269. m_scopeProducers.emplace_back(
  270. aznew RHI::ScopeProducerFunction<
  271. ScopeData,
  272. decltype(prepareFunction),
  273. decltype(compileFunction),
  274. decltype(executeFunction)>(
  275. occlusionScope,
  276. ScopeData{},
  277. prepareFunction,
  278. compileFunction,
  279. executeFunction));
  280. }
  281. void QueryExampleComponent::CreateScopeProducer()
  282. {
  283. using namespace AZ;
  284. struct ScopeData
  285. {
  286. };
  287. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  288. {
  289. {
  290. // Binds the swapchain color attachment.
  291. RHI::ImageScopeAttachmentDescriptor descriptor;
  292. descriptor.m_attachmentId = m_outputAttachmentId;
  293. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  294. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  295. frameGraph.UseColorAttachment(descriptor);
  296. }
  297. {
  298. // Binds the transient depth attachment
  299. auto depthAttachmentId = AZ::RHI::AttachmentId{ "DepthStencilID" };
  300. const AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
  301. AZ::RHI::ImageBindFlags::DepthStencil,
  302. static_cast<uint32_t>(GetViewportWidth()),
  303. static_cast<uint32_t>(GetViewportHeight()),
  304. AZ::RHI::Format::D32_FLOAT);
  305. const AZ::RHI::TransientImageDescriptor transientImageDescriptor(depthAttachmentId, imageDescriptor);
  306. frameGraph.GetAttachmentDatabase().CreateTransientImage(transientImageDescriptor);
  307. AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
  308. dsDesc.m_attachmentId = depthAttachmentId;
  309. dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
  310. dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
  311. dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
  312. frameGraph.UseDepthStencilAttachment(
  313. dsDesc, AZ::RHI::ScopeAttachmentAccess::Write,
  314. AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
  315. }
  316. // Query pools
  317. {
  318. frameGraph.UseQueryPool(
  319. m_occlusionQueryPool,
  320. RHI::Interval(m_currentOcclusionQueryIndex, m_currentOcclusionQueryIndex),
  321. m_currentType == QueryType::Predication ? RHI::QueryPoolScopeAttachmentType::Local : RHI::QueryPoolScopeAttachmentType::Global,
  322. RHI::ScopeAttachmentAccess::Write);
  323. if (m_timestampEnabled)
  324. {
  325. frameGraph.UseQueryPool(
  326. m_timeStampQueryPool,
  327. RHI::Interval(m_currentTimestampQueryIndex, m_currentTimestampQueryIndex + 1),
  328. RHI::QueryPoolScopeAttachmentType::Global,
  329. RHI::ScopeAttachmentAccess::Write);
  330. }
  331. if (m_pipelineStatisticsEnabled)
  332. {
  333. frameGraph.UseQueryPool(
  334. m_statisticsQueryPool,
  335. RHI::Interval(m_currentStatisticsQueryIndex, m_currentStatisticsQueryIndex),
  336. RHI::QueryPoolScopeAttachmentType::Global,
  337. RHI::ScopeAttachmentAccess::Write);
  338. }
  339. }
  340. // Add the predication buffer if necessary
  341. if (m_currentType == QueryType::Predication)
  342. {
  343. frameGraph.UseAttachment(
  344. m_predicationBufferAttachmentDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Predication,
  345. RHI::ScopeAttachmentStage::Predication);
  346. }
  347. // We will submit a single draw item.
  348. frameGraph.SetEstimatedItemCount(3);
  349. };
  350. using namespace AZ;
  351. const auto compileFunction = [this]([[maybe_unused]] const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  352. {
  353. for (auto& srg : m_shaderResourceGroups)
  354. {
  355. srg->Compile();
  356. }
  357. };
  358. const auto executeFunction = [this]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  359. {
  360. RHI::CommandList* commandList = context.GetCommandList();
  361. // Set persistent viewport and scissor state.
  362. commandList->SetViewports(&m_viewport, 1);
  363. commandList->SetScissors(&m_scissor, 1);
  364. {
  365. const RHI::DeviceIndexBufferView quadIndexBufferView = {
  366. *m_quadInputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()), offsetof(BufferData, m_indices),
  367. sizeof(BufferData::m_indices), RHI::IndexFormat::Uint16
  368. };
  369. RHI::DrawIndexed drawIndexed;
  370. drawIndexed.m_indexCount = 6;
  371. drawIndexed.m_instanceCount = 1;
  372. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  373. m_shaderResourceGroups[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  374. };
  375. RHI::DeviceDrawItem drawItem;
  376. drawItem.m_arguments = drawIndexed;
  377. drawItem.m_pipelineState = m_quadPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  378. drawItem.m_indexBufferView = &quadIndexBufferView;
  379. drawItem.m_streamBufferViewCount = 1;
  380. auto deviceStreamBufferView{m_quadStreamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex())};
  381. drawItem.m_streamBufferViews = &deviceStreamBufferView;
  382. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  383. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  384. // Draw possibly occluded quad
  385. {
  386. if (m_timestampEnabled)
  387. {
  388. m_timestampQueries[m_currentTimestampQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->WriteTimestamp(*commandList);
  389. m_timestampQueries[m_currentTimestampQueryIndex].m_isValid = true;
  390. }
  391. if (m_pipelineStatisticsEnabled)
  392. {
  393. m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->Begin(*commandList);
  394. }
  395. switch (m_currentType)
  396. {
  397. case QueryType::Occlusion:
  398. {
  399. // This is the oldest occlusion query (3 frames ago)
  400. uint64_t occlusionResults = 0;
  401. auto& queryEntry = m_occlusionQueries[(m_currentOcclusionQueryIndex + 1) % m_occlusionQueries.size()];
  402. if (queryEntry.m_isValid)
  403. {
  404. m_occlusionQueryPool->GetResults(queryEntry.m_query.get(), &occlusionResults, sizeof(occlusionResults), RHI::QueryResultFlagBits::Wait);
  405. }
  406. if (occlusionResults)
  407. {
  408. commandList->Submit(drawItem, 0);
  409. }
  410. break;
  411. }
  412. case QueryType::Predication:
  413. {
  414. commandList->BeginPredication(*m_predicationBuffer->GetDeviceBuffer(context.GetDeviceIndex()), 0, RHI::PredicationOp::EqualZero);
  415. commandList->Submit(drawItem, 0);
  416. commandList->EndPredication();
  417. break;
  418. }
  419. }
  420. if (m_pipelineStatisticsEnabled)
  421. {
  422. m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->End(*commandList);
  423. m_statisticsQueries[m_currentStatisticsQueryIndex].m_isValid = true;
  424. }
  425. if (m_timestampEnabled)
  426. {
  427. m_timestampQueries[m_currentTimestampQueryIndex + 1].m_query->GetDeviceQuery(context.GetDeviceIndex())->WriteTimestamp(*commandList);
  428. m_timestampQueries[m_currentTimestampQueryIndex + 1].m_isValid = true;
  429. }
  430. }
  431. // Draw occluding quad
  432. shaderResourceGroups[0] = m_shaderResourceGroups[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  433. commandList->Submit(drawItem, 1);
  434. // Draw quad to use for the oclussion query
  435. drawItem.m_pipelineState = m_boudingBoxPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  436. shaderResourceGroups[0] = m_shaderResourceGroups[2]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
  437. auto& queryEntry = m_occlusionQueries[m_currentOcclusionQueryIndex];
  438. queryEntry.m_query->GetDeviceQuery(context.GetDeviceIndex())->Begin(*commandList, m_precisionOcclusionEnabled ? RHI::QueryControlFlags::PreciseOcclusion : RHI::QueryControlFlags::None);
  439. commandList->Submit(drawItem, 2);
  440. queryEntry.m_query->GetDeviceQuery(context.GetDeviceIndex())->End(*commandList);
  441. queryEntry.m_isValid = true;
  442. }
  443. };
  444. const AZ::RHI::ScopeId OcclusionScope("Occlusion");
  445. m_scopeProducers.emplace_back(
  446. aznew RHI::ScopeProducerFunction<
  447. ScopeData,
  448. decltype(prepareFunction),
  449. decltype(compileFunction),
  450. decltype(executeFunction)>(
  451. OcclusionScope,
  452. ScopeData{},
  453. prepareFunction,
  454. compileFunction,
  455. executeFunction));
  456. }
  457. template<class T>
  458. void CreateQueries(AZ::RHI::Ptr<AZ::RHI::QueryPool>& queryPool, T& queries, AZ::RHI::QueryType type, AZ::RHI::PipelineStatisticsFlags statisticsMask = AZ::RHI::PipelineStatisticsFlags::None)
  459. {
  460. using namespace AZ;
  461. RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  462. const auto& features = device->GetFeatures();
  463. if (!RHI::CheckBitsAll(
  464. features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Graphics)],
  465. static_cast<RHI::QueryTypeFlags>(AZ_BIT(static_cast<uint32_t>(type)))))
  466. {
  467. return;
  468. }
  469. RHI::QueryPoolDescriptor queryPoolDesc;
  470. queryPoolDesc.m_queriesCount = static_cast<uint32_t>(queries.size());
  471. queryPoolDesc.m_type = type;
  472. queryPoolDesc.m_pipelineStatisticsMask = statisticsMask;
  473. queryPool = aznew RHI::QueryPool;
  474. auto result = queryPool->Init(queryPoolDesc);
  475. if (result != RHI::ResultCode::Success)
  476. {
  477. AZ_Assert(false, "Failed to createa query pool");
  478. return;
  479. }
  480. for (auto& queryEntry : queries)
  481. {
  482. queryEntry.m_query = aznew RHI::Query;
  483. queryPool->InitQuery(queryEntry.m_query.get());
  484. }
  485. }
  486. void QueryExampleComponent::CreateQueryResources()
  487. {
  488. CreateQueries(m_occlusionQueryPool, m_occlusionQueries, AZ::RHI::QueryType::Occlusion);
  489. CreateQueries(m_timeStampQueryPool, m_timestampQueries, AZ::RHI::QueryType::Timestamp);
  490. AZ::RHI::PipelineStatisticsFlags statisticsMask =
  491. AZ::RHI::PipelineStatisticsFlags::IAVertices | AZ::RHI::PipelineStatisticsFlags::VSInvocations |
  492. AZ::RHI::PipelineStatisticsFlags::IAPrimitives | AZ::RHI::PipelineStatisticsFlags::CInvocations |
  493. AZ::RHI::PipelineStatisticsFlags::CPrimitives | AZ::RHI::PipelineStatisticsFlags::PSInvocations;
  494. CreateQueries(m_statisticsQueryPool, m_statisticsQueries, AZ::RHI::QueryType::PipelineStatistics, statisticsMask);
  495. }
  496. void QueryExampleComponent::CreatePredicationResources()
  497. {
  498. using namespace AZ;
  499. RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  500. if (!device->GetFeatures().m_predication)
  501. {
  502. return;
  503. }
  504. m_predicationBufferPool = aznew RHI::BufferPool();
  505. RHI::BufferPoolDescriptor bufferPoolDesc;
  506. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::CopyWrite;
  507. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  508. m_predicationBufferPool->Init(bufferPoolDesc);
  509. m_predicationBuffer = aznew RHI::Buffer();
  510. RHI::BufferInitRequest request;
  511. request.m_buffer = m_predicationBuffer.get();
  512. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::CopyWrite, sizeof(uint64_t) };
  513. RHI::ResultCode result = m_predicationBufferPool->InitBuffer(request);
  514. if (result != RHI::ResultCode::Success)
  515. {
  516. AZ_Error(QueryExample::SampleName, false, "Failed to initialize position buffer with error code %d", result);
  517. return;
  518. }
  519. m_predicationBufferAttachmentDescriptor.m_attachmentId = QueryExample::PredicationBufferId;
  520. const uint64_t resultsCount = 1;
  521. m_predicationBufferAttachmentDescriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(
  522. 0,
  523. resultsCount,
  524. static_cast<uint32_t>(m_predicationBuffer->GetDescriptor().m_byteCount / resultsCount));
  525. }
  526. void QueryExampleComponent::SetQueryType(QueryType type)
  527. {
  528. if (type == m_currentType)
  529. {
  530. return;
  531. }
  532. m_currentType = type;
  533. m_elapsedTime = 0;
  534. if (m_currentType == QueryType::Predication)
  535. {
  536. m_precisionOcclusionEnabled = false;
  537. }
  538. }
  539. void QueryExampleComponent::DrawSampleSettings()
  540. {
  541. const auto& features = Utils::GetRHIDevice()->GetFeatures();
  542. ImGui::Spacing();
  543. AZStd::vector<const char*> items = { "Occlusion" };
  544. if (features.m_predication)
  545. {
  546. items.push_back("Predication");
  547. }
  548. int current_item = static_cast<int>(m_currentType);
  549. ImGui::Text("Type");
  550. ScriptableImGui::Combo("", &current_item, items.data(), static_cast<int>(items.size()));
  551. SetQueryType(static_cast<QueryType>(current_item));
  552. if (m_currentType == QueryType::Occlusion && features.m_occlusionQueryPrecise)
  553. {
  554. // Precision occlusion is only available for the Occlusion query type
  555. ScriptableImGui::Checkbox("Enable Precision occlusion", &m_precisionOcclusionEnabled);
  556. }
  557. auto& supportedQueries = features.m_queryTypesMask[static_cast<uint32_t>(AZ::RHI::HardwareQueueClass::Graphics)];
  558. if (AZ::RHI::CheckBitsAll(supportedQueries, AZ::RHI::QueryTypeFlags::Timestamp))
  559. {
  560. ScriptableImGui::Checkbox("Enable Timestamp queries", &m_timestampEnabled);
  561. }
  562. if (AZ::RHI::CheckBitsAll(supportedQueries, AZ::RHI::QueryTypeFlags::PipelineStatistics))
  563. {
  564. ScriptableImGui::Checkbox("Enable Statistics queries", &m_pipelineStatisticsEnabled);
  565. }
  566. if (m_precisionOcclusionEnabled)
  567. {
  568. uint64_t occlusionResults = 0;
  569. // This is the oldest occlusion query (3 frames ago).
  570. auto queryEntry = m_occlusionQueries[(m_currentOcclusionQueryIndex + 1) % m_occlusionQueries.size()];
  571. if (queryEntry.m_isValid)
  572. {
  573. m_occlusionQueryPool->GetResults(queryEntry.m_query.get(), &occlusionResults, sizeof(occlusionResults), AZ::RHI::QueryResultFlagBits::Wait);
  574. }
  575. ImGui::Text("Occlusion Result: %llu", static_cast<unsigned long long>(occlusionResults));
  576. }
  577. if(m_timestampEnabled)
  578. {
  579. // This is the oldest pair of timestamp queries (3 frames ago).
  580. size_t beginIndex = (m_currentTimestampQueryIndex + 2) % m_timestampQueries.size();
  581. uint64_t timestamps[2] = {};
  582. if (m_timestampQueries[beginIndex].m_isValid && m_timestampQueries[beginIndex + 1].m_isValid)
  583. {
  584. AZ::RHI::Query* queries[] = { m_timestampQueries[beginIndex].m_query.get() , m_timestampQueries[beginIndex + 1].m_query.get() };
  585. m_timeStampQueryPool->GetResults(queries, 2, timestamps, 2, AZ::RHI::QueryResultFlagBits::Wait);
  586. }
  587. if (ImGui::CollapsingHeader("Timestamp", ImGuiTreeNodeFlags_DefaultOpen))
  588. {
  589. AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  590. auto durationInMicroseconds = device->GpuTimestampToMicroseconds(timestamps[1] - timestamps[0], AZ::RHI::HardwareQueueClass::Graphics);
  591. ImGui::Indent();
  592. ImGui::Text("Draw time: %llu us", static_cast<unsigned long long>(durationInMicroseconds.count()));
  593. ImGui::Unindent();
  594. }
  595. }
  596. if (m_pipelineStatisticsEnabled)
  597. {
  598. AZStd::vector<uint64_t> statistics;
  599. uint32_t statisticsCount = AZ::RHI::CountBitsSet(static_cast<uint64_t>(m_statisticsQueryPool->GetDescriptor().m_pipelineStatisticsMask));
  600. statistics.resize(statisticsCount);
  601. size_t queryFrameIndex = (m_currentStatisticsQueryIndex + 1) % m_statisticsQueries.size();
  602. if (m_statisticsQueries[queryFrameIndex].m_isValid)
  603. {
  604. m_statisticsQueryPool->GetResults(m_statisticsQueries[queryFrameIndex].m_query.get(), statistics.data(), statisticsCount, AZ::RHI::QueryResultFlagBits::Wait);
  605. }
  606. const char* statisticsName[] =
  607. {
  608. "IAVertices",
  609. "IAPrimitives",
  610. "VSInvocations",
  611. "CInvocations",
  612. "CPrimitives",
  613. "PSInvocations"
  614. };
  615. if (ImGui::CollapsingHeader("Statistics", ImGuiTreeNodeFlags_DefaultOpen))
  616. {
  617. ImGui::Indent();
  618. for (size_t i = 0; i < statistics.size(); ++i)
  619. {
  620. ImGui::Text("%s: %llu\n", statisticsName[i], static_cast<unsigned long long>(statistics[i]));
  621. }
  622. ImGui::Unindent();
  623. }
  624. }
  625. m_imguiSidebar.End();
  626. }
  627. } // namespace AtomSampleViewer