Query.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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/Factory.h>
  9. #include <Atom/RHI/FrameGraphExecuteContext.h>
  10. #include <Atom/RHI/FrameGraphInterface.h>
  11. #include <Atom/RHI/Scope.h>
  12. #include <Atom/RPI.Public/GpuQuery/GpuQuerySystem.h>
  13. #include <Atom/RPI.Public/GpuQuery/Query.h>
  14. #include <Atom/RPI.Public/GpuQuery/QueryPool.h>
  15. #include <Atom/RHI/RHISystemInterface.h>
  16. #include <AzCore/std/parallel/scoped_lock.h>
  17. namespace AZ
  18. {
  19. namespace RPI
  20. {
  21. Query::Query(RPI::QueryPool* queryPool, RHI::Interval rhiQueryIndices, RHI::QueryType queryType, RHI::QueryPoolScopeAttachmentType attachmentType, RHI::ScopeAttachmentAccess attachmentAccess)
  22. {
  23. if (!queryPool)
  24. {
  25. AZ_Error("RPI::Query", false, "Query must be initialized with valid queryPool");
  26. return;
  27. }
  28. m_queryPool = queryPool;
  29. m_rhiQueryIndices = rhiQueryIndices;
  30. m_attachmentType = attachmentType;
  31. m_attachmentAccess = attachmentAccess;
  32. m_queryType = queryType;
  33. // Split the queryIndices into multiple intervals. One for each buffered frame.
  34. SubdivideRhiQueryIndices(rhiQueryIndices);
  35. }
  36. Query::~Query()
  37. {
  38. UnregisterFromPool();
  39. }
  40. void Query::UnregisterFromPool()
  41. {
  42. if (m_queryPool)
  43. {
  44. m_queryPool->UnregisterQuery(this);
  45. }
  46. }
  47. RHI::QueryType Query::GetQueryType() const
  48. {
  49. return m_queryType;
  50. }
  51. QueryResultCode Query::AddToFrameGraph(RHI::FrameGraphInterface frameGraph)
  52. {
  53. // Assign the FrameIndex of the query pool.
  54. if (!AssignNewFrameIndexToSubQuery(m_queryPool->GetPoolFrameIndex()))
  55. {
  56. return QueryResultCode::Fail;
  57. }
  58. // Get the RHI Query indices from the current FrameIndex.
  59. const auto rhiQueryIndices = GetRhiQueryIndicesFromCurrentFrame();
  60. if (!rhiQueryIndices)
  61. {
  62. return QueryResultCode::Fail;
  63. }
  64. // Tell the FrameGraph which RHI QueryPool, and which RHI Queries need to be used.
  65. [[maybe_unused]] RHI::ResultCode resultCode = frameGraph.UseQueryPool(m_queryPool->m_rhiQueryPool, rhiQueryIndices.value(), m_attachmentType, m_attachmentAccess);
  66. AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to add the queries to the scope builder");
  67. // Invalidate the ScopeId.
  68. m_cachedScopeId = RHI::ScopeId();
  69. return QueryResultCode::Success;
  70. }
  71. QueryResultCode Query::BeginQuery(const RHI::FrameGraphExecuteContext& context)
  72. {
  73. // Return fail if the query wasn't added to frame graph
  74. if (m_cachedSubQueryArrayIndex == InvalidQueryIndex)
  75. {
  76. return QueryResultCode::Fail;
  77. }
  78. const auto rhiQueryIndices = GetRhiQueryIndicesFromCurrentFrame();
  79. if (!rhiQueryIndices)
  80. {
  81. return QueryResultCode::Fail;
  82. }
  83. [[maybe_unused]] RHI::ResultCode resultCode = m_queryPool->BeginQueryInternal(rhiQueryIndices.value(), *context.GetCommandList());
  84. AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to begin recording the query");
  85. m_cachedScopeId = context.GetScopeId();
  86. return QueryResultCode::Success;
  87. }
  88. QueryResultCode Query::EndQuery(const RHI::FrameGraphExecuteContext& context)
  89. {
  90. // return fail if the query wasn't added to frame graph
  91. if (m_cachedSubQueryArrayIndex == InvalidQueryIndex)
  92. {
  93. return QueryResultCode::Fail;
  94. }
  95. // Validate that the queries are recorded for the same scope.
  96. // Note: the timestamp query skips this check because its start and end may be added in randome order since they are added in different FrameGraphExecuteContext.
  97. // The order doesn't matter because timestamp's begin or end are just inserting a timestamp to the command list.
  98. // Also, the command list's execution order will still follow the order of start query and end query.
  99. if (m_cachedScopeId != context.GetScopeId() && GetQueryType() != RHI::QueryType::Timestamp)
  100. {
  101. AZ_Warning("RPI::Query", false,
  102. "The FrameGraphExecuteContext's Scope that is used for RPI::Query::BeginQuery is not the same for RPI::Query::EndQuery.");
  103. return QueryResultCode::Fail;
  104. }
  105. const auto rhiQueryIndices = GetRhiQueryIndicesFromCurrentFrame();
  106. if (!rhiQueryIndices)
  107. {
  108. return QueryResultCode::Fail;
  109. }
  110. [[maybe_unused]] RHI::ResultCode resultCode = m_queryPool->EndQueryInternal(rhiQueryIndices.value(), *context.GetCommandList());
  111. AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to end recording the query");
  112. return QueryResultCode::Success;
  113. }
  114. QueryResultCode Query::GetLatestResultAndWait(void* queryResult, uint32_t resultSizeInBytes)
  115. {
  116. if (resultSizeInBytes < m_queryPool->GetQueryResultSize())
  117. {
  118. AZ_Warning("RPI::Query", false, "Not enough space to copy the query result into the provided data container.");
  119. return QueryResultCode::Fail;
  120. }
  121. // Get the most recent query index that has been submitted at least 1 frame ago.
  122. const uint64_t frameOffset = 1u;
  123. const uint32_t recentSubQueryIndex = GetMostRecentSubQueryArrayIndex(frameOffset);
  124. if (recentSubQueryIndex == InvalidQueryIndex)
  125. {
  126. return QueryResultCode::Fail;
  127. }
  128. const SubQuery& recentSubQuery = m_subQueryArray[recentSubQueryIndex];
  129. // This may stall the calling thread; depending if the query result is already available for polling.
  130. return m_queryPool->GetQueryResultFromIndices(static_cast<uint64_t*>(queryResult), recentSubQuery.m_rhiQueryIndices, RHI::QueryResultFlagBits::Wait);
  131. }
  132. QueryResultCode Query::GetLatestResult(void* queryResult, uint32_t resultSizeInBytes)
  133. {
  134. if (resultSizeInBytes < m_queryPool->GetQueryResultSize())
  135. {
  136. AZ_Warning("RPI::Query", false, "Not enough space to copy the query result into the provided data container.");
  137. return QueryResultCode::Fail;
  138. }
  139. // Get the most recent query index that has been submitted at least (BufferedFrames-1) frames ago.
  140. const uint32_t frameOffset = BufferedFrames - 1u;
  141. const uint32_t latestQueryIndex = GetMostRecentSubQueryArrayIndex(frameOffset);
  142. if (latestQueryIndex == InvalidQueryIndex)
  143. {
  144. return QueryResultCode::Fail;
  145. }
  146. SubQuery& subQuery = m_subQueryArray[latestQueryIndex];
  147. return m_queryPool->GetQueryResultFromIndices(static_cast<uint64_t*>(queryResult), subQuery.m_rhiQueryIndices, RHI::QueryResultFlagBits::None);
  148. }
  149. bool Query::AssignNewFrameIndexToSubQuery(uint64_t poolFrameIndex)
  150. {
  151. #if defined (AZ_RPI_ENABLE_VALIDATION)
  152. // Check if the query is already added in this frame.
  153. {
  154. const auto predicate = [poolFrameIndex](SubQuery& queryIndices)
  155. {
  156. return queryIndices.m_poolFrameIndex == poolFrameIndex;
  157. };
  158. if (AZStd::any_of(m_subQueryArray.begin(), m_subQueryArray.end(), predicate))
  159. {
  160. AZ_Warning("RPI::Query", false, "Query is already added in this frame");
  161. return false;
  162. }
  163. }
  164. #else
  165. // Check if the FrameIndex is already present for this query instance, meaning that
  166. // the user is trying to use the same query multiple times to record within a single frame.
  167. if (m_cachedSubQueryArrayIndex != InvalidQueryIndex &&
  168. m_subQueryArray[m_cachedSubQueryArrayIndex].m_poolFrameIndex == poolFrameIndex)
  169. {
  170. AZ_Warning("RPI::Query", false, "Query is already added in this frame");
  171. return false;
  172. }
  173. #endif
  174. // Get the oldest query array index.
  175. const uint32_t availableQueryIndex = GetOldestOrAvailableSubQueryArrayIndex();
  176. // Cache the index of the most recently added SubQuery.
  177. m_cachedSubQueryArrayIndex = availableQueryIndex;
  178. // Reuse the oldest query by setting the current FrameIndex.
  179. m_subQueryArray[availableQueryIndex].m_poolFrameIndex = poolFrameIndex;
  180. return true;
  181. }
  182. void Query::SubdivideRhiQueryIndices(RHI::Interval rhiQueryIndices)
  183. {
  184. // Calculate the amount of RHI Queries used for this RPI Query.
  185. const uint32_t queryIndicesCount = rhiQueryIndices.m_max - rhiQueryIndices.m_min + 1u;
  186. AZ_Assert((queryIndicesCount % m_queryPool->GetQueriesPerResult()) == 0u,
  187. "The amount of RHI::Query indices used for the RPI::Query is not a multiple of the number of RHI::Queries required to calculate a single result.");
  188. // Calculate the number of query groups.
  189. const uint32_t subQueryIndexCount = queryIndicesCount / m_queryPool->GetQueriesPerResult();
  190. AZ_Assert(subQueryIndexCount == BufferedFrames,
  191. "The amount of QueryGroups needs to be equal to the defined BufferedFrames");
  192. // Divide the RHI Query indices equally among the SubQueries.
  193. for (uint32_t queryGroupIndex = 0u; queryGroupIndex < subQueryIndexCount; queryGroupIndex++)
  194. {
  195. // Calculate the range of the interval.
  196. const uint32_t groupOffset = queryGroupIndex * m_queryPool->GetQueriesPerResult();
  197. const uint32_t min = groupOffset + rhiQueryIndices.m_min;
  198. const uint32_t max = groupOffset + rhiQueryIndices.m_min + m_queryPool->GetQueriesPerResult() - 1u;
  199. m_subQueryArray[queryGroupIndex].m_rhiQueryIndices = RHI::Interval(min, max);
  200. }
  201. }
  202. template<typename T>
  203. uint32_t Query::ReturnSubQueryArrayIndex(T&& comp, uint64_t initialCachedDelta, bool returnOnInvalidIndex) const
  204. {
  205. uint32_t cachedQueryIndex = InvalidQueryIndex;
  206. uint64_t cachedFrameDelta = initialCachedDelta;
  207. for (uint32_t i = 0u; i < m_subQueryArray.size(); i++)
  208. {
  209. const SubQuery& subQuery = m_subQueryArray[i];
  210. // Return or ignore the index of reused SubQueries.
  211. if (subQuery.m_poolFrameIndex == SubQuery::InvalidFrameIndex)
  212. {
  213. if (returnOnInvalidIndex)
  214. {
  215. return i;
  216. }
  217. else
  218. {
  219. continue;
  220. }
  221. }
  222. // Calculate the delta between the RPI QueryPool's FrameIndex and the SubQuery's cached FrameIndex.
  223. const uint64_t poolFrameIndex = m_queryPool->GetPoolFrameIndex();
  224. AZ_Assert(poolFrameIndex >= subQuery.m_poolFrameIndex, "The SubQuery's cached FrameIndex is older than the RPI QueryPool's FrameIndex");
  225. const uint64_t frameDelta = poolFrameIndex - subQuery.m_poolFrameIndex;
  226. if (comp(frameDelta, cachedFrameDelta))
  227. {
  228. cachedQueryIndex = i;
  229. cachedFrameDelta = frameDelta;
  230. }
  231. }
  232. return cachedQueryIndex;
  233. }
  234. uint32_t Query::GetMostRecentSubQueryArrayIndex(uint64_t threshold) const
  235. {
  236. return ReturnSubQueryArrayIndex([threshold](uint64_t frameDelta, uint64_t cachedFrameDelta)
  237. {
  238. return frameDelta < cachedFrameDelta && frameDelta >= threshold;
  239. }, SubQuery::InvalidFrameIndex, false);
  240. }
  241. uint32_t Query::GetOldestOrAvailableSubQueryArrayIndex() const
  242. {
  243. return ReturnSubQueryArrayIndex([](uint64_t frameDelta, uint64_t cachedFrameDelta)
  244. {
  245. return frameDelta > cachedFrameDelta;
  246. }, 0u, true);
  247. }
  248. AZStd::optional<RHI::Interval> Query::GetRhiQueryIndicesFromCurrentFrame() const
  249. {
  250. if (m_cachedSubQueryArrayIndex == InvalidQueryIndex)
  251. {
  252. return AZStd::nullopt;
  253. }
  254. const SubQuery& subQuery = m_subQueryArray[m_cachedSubQueryArrayIndex];
  255. const uint64_t poolFrameIndex = m_queryPool->GetPoolFrameIndex();
  256. if (poolFrameIndex != subQuery.m_poolFrameIndex)
  257. {
  258. AZ_Warning("RPI::Query", false, "FrameIndex doesn't match the one from the query. The recording of a query needs to happen within one frame");
  259. return AZStd::nullopt;
  260. }
  261. return subQuery.m_rhiQueryIndices;
  262. }
  263. }; // Namespace RPI
  264. }; // Namespace AZ