MultiDeviceQueryTests.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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 "RHITestFixture.h"
  9. #include <Atom/RHI/FrameEventBus.h>
  10. #include <Atom/RHI/QueryPool.h>
  11. #include <AzCore/std/containers/bitset.h>
  12. #include <Tests/Device.h>
  13. namespace UnitTest
  14. {
  15. using namespace AZ;
  16. class MultiDeviceQueryTests : public MultiDeviceRHITestFixture
  17. {
  18. public:
  19. MultiDeviceQueryTests()
  20. : MultiDeviceRHITestFixture()
  21. {
  22. }
  23. private:
  24. void SetUp() override
  25. {
  26. MultiDeviceRHITestFixture::SetUp();
  27. }
  28. void TearDown() override
  29. {
  30. MultiDeviceRHITestFixture::TearDown();
  31. }
  32. };
  33. TEST_F(MultiDeviceQueryTests, TestNoop)
  34. {
  35. RHI::Ptr<RHI::Query> noopQuery;
  36. noopQuery = aznew RHI::Query;
  37. AZ_TEST_ASSERT(noopQuery);
  38. RHI::Ptr<RHI::QueryPool> noopQueryPool;
  39. noopQueryPool = aznew RHI::QueryPool;
  40. AZ_TEST_ASSERT(noopQueryPool);
  41. }
  42. TEST_F(MultiDeviceQueryTests, Test)
  43. {
  44. RHI::Ptr<RHI::Query> queryA;
  45. queryA = aznew RHI::Query;
  46. queryA->SetName(Name("QueryA"));
  47. AZ_TEST_ASSERT(queryA->GetName().GetStringView() == "QueryA");
  48. AZ_TEST_ASSERT(queryA->use_count() == 1);
  49. {
  50. RHI::Ptr<RHI::QueryPool> queryPool;
  51. queryPool = aznew RHI::QueryPool;
  52. EXPECT_EQ(1, queryPool->use_count());
  53. RHI::Ptr<RHI::Query> queryB;
  54. queryB = aznew RHI::Query;
  55. EXPECT_EQ(1, queryB->use_count());
  56. RHI::QueryPoolDescriptor queryPoolDesc;
  57. queryPoolDesc.m_queriesCount = 2;
  58. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  59. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  60. queryPool->Init(DeviceMask, queryPoolDesc);
  61. EXPECT_FALSE(queryA->IsInitialized());
  62. EXPECT_FALSE(queryB->IsInitialized());
  63. queryPool->InitQuery(queryA.get());
  64. EXPECT_EQ(1, queryA->use_count());
  65. EXPECT_TRUE(queryA->IsInitialized());
  66. queryPool->InitQuery(queryB.get());
  67. EXPECT_TRUE(queryB->IsInitialized());
  68. EXPECT_EQ(queryA->GetPool(), queryPool.get());
  69. EXPECT_EQ(queryB->GetPool(), queryPool.get());
  70. EXPECT_EQ(queryPool->GetResourceCount(), 2);
  71. {
  72. uint32_t queryIndex = 0;
  73. const RHI::Query* queries[] = { queryA.get(), queryB.get() };
  74. queryPool->ForEach<RHI::Query>(
  75. [&queryIndex, &queries]([[maybe_unused]] RHI::Query& query)
  76. {
  77. AZ_UNUSED(queries); // Prevent unused warning in release builds
  78. AZ_Assert(queries[queryIndex] == &query, "Queries don't match");
  79. queryIndex++;
  80. });
  81. }
  82. queryB->Shutdown();
  83. EXPECT_EQ(queryB->GetPool(), nullptr);
  84. RHI::Ptr<RHI::QueryPool> queryPoolB;
  85. queryPoolB = aznew RHI::QueryPool;
  86. queryPoolB->Init(DeviceMask, queryPoolDesc);
  87. queryPoolB->InitQuery(queryB.get());
  88. EXPECT_EQ(queryB->GetPool(), queryPoolB.get());
  89. // Since we are switching queryPool for queryB it adds a refcount and invalidates the views.
  90. // We need this to ensure the views are fully invalidated in order to release the refcount and avoid a leak.
  91. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  92. queryPoolB->Shutdown();
  93. EXPECT_EQ(queryPoolB->GetResourceCount(), 0);
  94. }
  95. EXPECT_EQ(queryA->GetPool(), nullptr);
  96. EXPECT_EQ(queryA->use_count(), 1);
  97. }
  98. TEST_F(MultiDeviceQueryTests, TestAllocations)
  99. {
  100. static const uint32_t numQueries = 10;
  101. AZStd::array<RHI::Ptr<RHI::Query>, numQueries> queries;
  102. for (auto& query : queries)
  103. {
  104. query = aznew RHI::Query;
  105. }
  106. RHI::Ptr<RHI::QueryPool> queryPool;
  107. queryPool = aznew RHI::QueryPool;
  108. RHI::QueryPoolDescriptor queryPoolDesc;
  109. queryPoolDesc.m_queriesCount = numQueries;
  110. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  111. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  112. queryPool->Init(DeviceMask, queryPoolDesc);
  113. AZStd::vector<RHI::Query*> queriesToInitialize(numQueries);
  114. for (size_t i = 0; i < queries.size(); ++i)
  115. {
  116. queriesToInitialize[i] = queries[i].get();
  117. }
  118. RHI::ResultCode result = queryPool->InitQuery(queriesToInitialize.data(), static_cast<uint32_t>(queriesToInitialize.size()));
  119. EXPECT_EQ(result, RHI::ResultCode::Success);
  120. auto checkSlotsFunc = [](const AZStd::vector<RHI::Query*>& queries)
  121. {
  122. if (queries.size() < 2)
  123. {
  124. return true;
  125. }
  126. AZStd::vector<uint32_t> indices;
  127. for (auto& query : queries)
  128. {
  129. indices.push_back(query->GetHandle(AZ::RHI::MultiDevice::DefaultDeviceIndex).GetIndex());
  130. }
  131. AZStd::sort(indices.begin(), indices.end());
  132. for (size_t i = 0; i < indices.size() - 1; ++i)
  133. {
  134. if (indices[i] != (indices[i + 1] + 1))
  135. {
  136. return false;
  137. }
  138. }
  139. return true;
  140. };
  141. checkSlotsFunc(queriesToInitialize);
  142. RHI::Ptr<RHI::Query> extraQuery = aznew RHI::Query;
  143. EXPECT_EQ(queryPool->InitQuery(extraQuery.get()), RHI::ResultCode::OutOfMemory);
  144. AZ_TEST_ASSERT(!extraQuery->IsInitialized());
  145. AZStd::vector<uint32_t> queriesIndicesToShutdown = { 5, 6 };
  146. AZStd::vector<RHI::Query*> queriesToShutdown;
  147. for (auto& index : queriesIndicesToShutdown)
  148. {
  149. queries[index]->Shutdown();
  150. queriesToShutdown.push_back(queries[index].get());
  151. }
  152. EXPECT_EQ(queryPool->GetResourceCount(), numQueries - static_cast<uint32_t>(queriesIndicesToShutdown.size()));
  153. result = queryPool->InitQuery(queriesToShutdown.data(), static_cast<uint32_t>(queriesIndicesToShutdown.size()));
  154. EXPECT_EQ(result, RHI::ResultCode::Success);
  155. checkSlotsFunc(queriesToShutdown);
  156. queriesIndicesToShutdown = { 2, 5, 9 };
  157. queriesToShutdown.clear();
  158. for (auto& index : queriesIndicesToShutdown)
  159. {
  160. queries[index]->Shutdown();
  161. queriesToShutdown.push_back(queries[index].get());
  162. }
  163. EXPECT_EQ(queryPool->GetResourceCount(), (numQueries - static_cast<uint32_t>(queriesIndicesToShutdown.size())));
  164. result = queryPool->InitQuery(queriesToShutdown.data(), static_cast<uint32_t>(queriesToShutdown.size()));
  165. // Since we are switching queryPools for some queries it adds a refcount and invalidates the views.
  166. // We need to ensure the views are fully invalidated in order to release the refcount and avoid leaks.
  167. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  168. checkSlotsFunc(queriesToInitialize);
  169. }
  170. TEST_F(MultiDeviceQueryTests, TestIntervals)
  171. {
  172. static const uint32_t numQueries = 10;
  173. AZStd::array<RHI::Ptr<RHI::Query>, numQueries> queries;
  174. for (auto& query : queries)
  175. {
  176. query = aznew RHI::Query;
  177. }
  178. RHI::Ptr<RHI::QueryPool> queryPool;
  179. queryPool = aznew RHI::QueryPool;
  180. RHI::QueryPoolDescriptor queryPoolDesc;
  181. queryPoolDesc.m_queriesCount = numQueries;
  182. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  183. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  184. queryPool->Init(DeviceMask, queryPoolDesc);
  185. AZStd::vector<RHI::Query*> queriesToInitialize(numQueries);
  186. for (size_t i = 0; i < queries.size(); ++i)
  187. {
  188. queriesToInitialize[i] = queries[i].get();
  189. }
  190. RHI::ResultCode result = queryPool->InitQuery(queriesToInitialize.data(), static_cast<uint32_t>(queriesToInitialize.size()));
  191. EXPECT_EQ(result, RHI::ResultCode::Success);
  192. uint64_t results[numQueries * DeviceCount] = {};
  193. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  194. {
  195. auto* testQueryPool = static_cast<UnitTest::QueryPool*>(queryPool->GetDeviceQueryPool(deviceIndex).get());
  196. auto& testQueryPoolIntervals = testQueryPool->m_calledIntervals;
  197. testQueryPoolIntervals.clear();
  198. EXPECT_EQ(queryPool->GetResults(results, numQueries * DeviceCount, RHI::QueryResultFlagBits::None), RHI::ResultCode::Success);
  199. EXPECT_EQ(testQueryPoolIntervals.size(), 1);
  200. EXPECT_EQ(testQueryPoolIntervals.front(), RHI::Interval(0, numQueries - 1));
  201. testQueryPoolIntervals.clear();
  202. auto* queryToTest = queries[5].get();
  203. EXPECT_EQ(queryPool->GetResults(queryToTest, results, DeviceCount, RHI::QueryResultFlagBits::None), RHI::ResultCode::Success);
  204. EXPECT_EQ(testQueryPoolIntervals.size(), 1);
  205. EXPECT_EQ(
  206. testQueryPoolIntervals.front(),
  207. RHI::Interval(queryToTest->GetHandle(deviceIndex).GetIndex(), queryToTest->GetHandle(deviceIndex).GetIndex()));
  208. AZStd::vector<RHI::Interval> intervalsToTest = { RHI::Interval(5, 5), RHI::Interval(0, 3), RHI::Interval(8, 9) };
  209. AZStd::vector<RHI::Query*> queriesToTest;
  210. for (auto& interval : intervalsToTest)
  211. {
  212. for (uint32_t i = interval.m_min; i <= interval.m_max; ++i)
  213. {
  214. queriesToTest.push_back(queries[i].get());
  215. }
  216. }
  217. testQueryPoolIntervals.clear();
  218. EXPECT_EQ(
  219. queryPool->GetResults(
  220. queriesToTest.data(), static_cast<uint32_t>(queriesToTest.size()), results, numQueries * DeviceCount, RHI::QueryResultFlagBits::None),
  221. RHI::ResultCode::Success);
  222. EXPECT_EQ(testQueryPoolIntervals.size(), intervalsToTest.size());
  223. for (auto& interval : intervalsToTest)
  224. {
  225. auto foundIt = AZStd::find(testQueryPoolIntervals.begin(), testQueryPoolIntervals.end(), interval);
  226. EXPECT_NE(foundIt, testQueryPoolIntervals.end());
  227. }
  228. }
  229. }
  230. TEST_F(MultiDeviceQueryTests, TestQuery)
  231. {
  232. AZStd::array<RHI::Ptr<RHI::QueryPool>, RHI::QueryTypeCount> queryPools;
  233. for (size_t i = 0; i < queryPools.size(); ++i)
  234. {
  235. auto& queryPool = queryPools[i];
  236. queryPool = aznew RHI::QueryPool;
  237. RHI::QueryPoolDescriptor queryPoolDesc;
  238. queryPoolDesc.m_queriesCount = 1;
  239. queryPoolDesc.m_type = static_cast<RHI::QueryType>(i);
  240. queryPoolDesc.m_pipelineStatisticsMask = queryPoolDesc.m_type == RHI::QueryType::PipelineStatistics
  241. ? RHI::PipelineStatisticsFlags::CInvocations
  242. : RHI::PipelineStatisticsFlags::None;
  243. queryPool->Init(DeviceMask, queryPoolDesc);
  244. }
  245. auto& occlusionQueryPool = queryPools[static_cast<uint32_t>(RHI::QueryType::Occlusion)];
  246. auto& timestampQueryPool = queryPools[static_cast<uint32_t>(RHI::QueryType::Timestamp)];
  247. auto& statisticsQueryPool = queryPools[static_cast<uint32_t>(RHI::QueryType::PipelineStatistics)];
  248. uint64_t data;
  249. RHI::CommandList& dummyCommandList = reinterpret_cast<RHI::CommandList&>(data);
  250. // Correct begin and end for occlusion
  251. {
  252. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  253. EXPECT_EQ(occlusionQueryPool->InitQuery(query.get()), RHI::ResultCode::Success);
  254. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  255. {
  256. EXPECT_EQ(query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList), RHI::ResultCode::Success);
  257. EXPECT_EQ(query->GetDeviceQuery(deviceIndex)->End(dummyCommandList), RHI::ResultCode::Success);
  258. }
  259. }
  260. // Double Begin
  261. {
  262. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  263. occlusionQueryPool->InitQuery(query.get());
  264. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  265. {
  266. query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList);
  267. AZ_TEST_START_ASSERTTEST;
  268. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList));
  269. AZ_TEST_STOP_ASSERTTEST(1);
  270. }
  271. }
  272. // End without Begin
  273. {
  274. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  275. occlusionQueryPool->InitQuery(query.get());
  276. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  277. {
  278. AZ_TEST_START_ASSERTTEST;
  279. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->End(dummyCommandList));
  280. AZ_TEST_STOP_ASSERTTEST(1);
  281. }
  282. }
  283. // End with another command list
  284. {
  285. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  286. occlusionQueryPool->InitQuery(query.get());
  287. uint64_t anotherData;
  288. RHI::CommandList& anotherDummyCmdList = reinterpret_cast<RHI::CommandList&>(anotherData);
  289. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  290. {
  291. EXPECT_EQ(RHI::ResultCode::Success, query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList));
  292. AZ_TEST_START_ASSERTTEST;
  293. EXPECT_EQ(RHI::ResultCode::InvalidArgument, query->GetDeviceQuery(deviceIndex)->End(anotherDummyCmdList));
  294. AZ_TEST_STOP_ASSERTTEST(1);
  295. }
  296. }
  297. // Invalid flag
  298. {
  299. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  300. statisticsQueryPool->InitQuery(query.get());
  301. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  302. {
  303. AZ_TEST_START_ASSERTTEST;
  304. EXPECT_EQ(
  305. RHI::ResultCode::InvalidArgument,
  306. query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList, RHI::QueryControlFlags::PreciseOcclusion));
  307. AZ_TEST_STOP_ASSERTTEST(1);
  308. }
  309. }
  310. // Invalid Begin for Timestamp
  311. {
  312. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  313. timestampQueryPool->InitQuery(query.get());
  314. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  315. {
  316. AZ_TEST_START_ASSERTTEST;
  317. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList));
  318. AZ_TEST_STOP_ASSERTTEST(1);
  319. }
  320. }
  321. // Invalid End for Timestamp
  322. {
  323. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  324. timestampQueryPool->InitQuery(query.get());
  325. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  326. {
  327. AZ_TEST_START_ASSERTTEST;
  328. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->End(dummyCommandList));
  329. AZ_TEST_STOP_ASSERTTEST(1);
  330. }
  331. }
  332. // Invalid WriteTimestamp
  333. {
  334. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  335. occlusionQueryPool->InitQuery(query.get());
  336. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  337. {
  338. AZ_TEST_START_ASSERTTEST;
  339. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->WriteTimestamp(dummyCommandList));
  340. AZ_TEST_STOP_ASSERTTEST(1);
  341. }
  342. }
  343. // Correct WriteTimestamp
  344. {
  345. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  346. timestampQueryPool->InitQuery(query.get());
  347. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  348. {
  349. EXPECT_EQ(RHI::ResultCode::Success, query->GetDeviceQuery(deviceIndex)->WriteTimestamp(dummyCommandList));
  350. }
  351. }
  352. }
  353. TEST_F(MultiDeviceQueryTests, TestQueryPoolInitialization)
  354. {
  355. RHI::Ptr<RHI::QueryPool> queryPool;
  356. queryPool = aznew RHI::QueryPool;
  357. RHI::QueryPoolDescriptor queryPoolDesc;
  358. queryPoolDesc.m_queriesCount = 0;
  359. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  360. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  361. // Count of 0
  362. AZ_TEST_START_ASSERTTEST;
  363. EXPECT_EQ(queryPool->Init(DeviceMask, queryPoolDesc), RHI::ResultCode::InvalidArgument);
  364. AZ_TEST_STOP_ASSERTTEST(1);
  365. // valid m_pipelineStatisticsMask for Occlusion QueryType
  366. queryPoolDesc.m_queriesCount = 1;
  367. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::CInvocations;
  368. EXPECT_EQ(queryPool->Init(DeviceMask, queryPoolDesc), RHI::ResultCode::Success);
  369. // invalid m_pipelineStatisticsMask for PipelineStatistics QueryType
  370. queryPoolDesc.m_type = RHI::QueryType::PipelineStatistics;
  371. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  372. AZ_TEST_START_ASSERTTEST;
  373. EXPECT_EQ(queryPool->Init(DeviceMask, queryPoolDesc), RHI::ResultCode::InvalidArgument);
  374. AZ_TEST_STOP_ASSERTTEST(1);
  375. }
  376. TEST_F(MultiDeviceQueryTests, TestResults)
  377. {
  378. AZStd::array<RHI::Ptr<RHI::QueryPool>, 2> queryPools;
  379. RHI::PipelineStatisticsFlags mask = RHI::PipelineStatisticsFlags::CInvocations | RHI::PipelineStatisticsFlags::CPrimitives |
  380. RHI::PipelineStatisticsFlags::IAPrimitives;
  381. for (auto& queryPool : queryPools)
  382. {
  383. queryPool = aznew RHI::QueryPool;
  384. RHI::QueryPoolDescriptor queryPoolDesc;
  385. queryPoolDesc.m_queriesCount = 2;
  386. queryPoolDesc.m_type = RHI::QueryType::PipelineStatistics;
  387. queryPoolDesc.m_pipelineStatisticsMask = mask;
  388. EXPECT_EQ(queryPool->Init(DeviceMask, queryPoolDesc), RHI::ResultCode::Success);
  389. }
  390. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  391. uint32_t numPipelineStatistics = RHI::CountBitsSet(static_cast<uint64_t>(mask));
  392. AZStd::vector<uint64_t> results(numPipelineStatistics * 2 * DeviceCount);
  393. // Using uninitialized query
  394. AZ_TEST_START_ASSERTTEST;
  395. EXPECT_EQ(
  396. queryPools[0]->GetResults(results.data(), numPipelineStatistics * 2 * DeviceCount, RHI::QueryResultFlagBits::None),
  397. RHI::ResultCode::InvalidArgument);
  398. AZ_TEST_STOP_ASSERTTEST(3);
  399. // Wrong size for results count.
  400. queryPools[0]->InitQuery(query.get());
  401. AZ_TEST_START_ASSERTTEST;
  402. EXPECT_EQ(queryPools[0]->GetResults(results.data(), DeviceCount, RHI::QueryResultFlagBits::None), RHI::ResultCode::InvalidArgument);
  403. AZ_TEST_STOP_ASSERTTEST(1);
  404. // Using a query from another pool
  405. RHI::Ptr<RHI::Query> anotherQuery = aznew RHI::Query;
  406. queryPools[1]->InitQuery(anotherQuery.get());
  407. AZ_TEST_START_ASSERTTEST;
  408. EXPECT_EQ(
  409. queryPools[0]->GetResults(anotherQuery.get(), results.data(), numPipelineStatistics * DeviceCount, RHI::QueryResultFlagBits::None),
  410. RHI::ResultCode::InvalidArgument);
  411. AZ_TEST_STOP_ASSERTTEST(1);
  412. // Results count is too small
  413. anotherQuery->Shutdown();
  414. queryPools[0]->InitQuery(anotherQuery.get());
  415. RHI::Query* queries[] = { query.get(), anotherQuery.get() };
  416. AZ_TEST_START_ASSERTTEST;
  417. EXPECT_EQ(
  418. queryPools[0]->GetResults(queries, 2, results.data(), numPipelineStatistics * DeviceCount, RHI::QueryResultFlagBits::None),
  419. RHI::ResultCode::InvalidArgument);
  420. AZ_TEST_STOP_ASSERTTEST(1);
  421. // Correct usage
  422. EXPECT_EQ(
  423. queryPools[0]->GetResults(queries, 2, results.data(), numPipelineStatistics * 5 * DeviceCount, RHI::QueryResultFlagBits::None),
  424. RHI::ResultCode::Success);
  425. // Unsorted queries
  426. {
  427. const size_t numQueries = 5;
  428. AZStd::array<RHI::Ptr<AZ::RHI::Query>, numQueries> queries2;
  429. AZStd::vector<uint64_t> results2(numQueries * DeviceCount);
  430. RHI::Ptr<RHI::QueryPool> queryPool = aznew RHI::QueryPool;
  431. RHI::QueryPoolDescriptor queryPoolDesc;
  432. queryPoolDesc.m_queriesCount = 5;
  433. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  434. EXPECT_EQ(queryPool->Init(DeviceMask, queryPoolDesc), RHI::ResultCode::Success);
  435. for (size_t i = 0; i < queries2.size(); ++i)
  436. {
  437. queries2[i] = aznew RHI::Query;
  438. queryPool->InitQuery(queries2[i].get());
  439. }
  440. AZStd::array<RHI::Query*, numQueries> queriesPtr = {
  441. queries2[2].get(), queries2[0].get(), queries2[1].get(), queries2[3].get(), queries2[4].get()
  442. };
  443. EXPECT_EQ(
  444. queryPool->GetResults(queriesPtr.data(), numQueries, results2.data(), numQueries * DeviceCount, RHI::QueryResultFlagBits::None),
  445. RHI::ResultCode::Success);
  446. for (uint32_t i = 0; i < numQueries; ++i)
  447. {
  448. EXPECT_EQ(results2[i], queriesPtr[i]->GetHandle(AZ::RHI::MultiDevice::DefaultDeviceIndex).GetIndex());
  449. }
  450. }
  451. // Since we are switching queryPools for some queries it adds a refcount and invalidates the views.
  452. // We need to ensure the views are fully invalidated in order to release the refcount and avoid leaks.
  453. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  454. }
  455. } // namespace UnitTest