MultiDeviceQueryTests.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  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(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(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(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. // Since we are recreating queries it adds a refcount and invalidates the (non-existing) views.
  157. // We need to ensure to release the refcount and avoid leaks by running the invalidate bus.
  158. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  159. queriesIndicesToShutdown = { 2, 5, 9 };
  160. queriesToShutdown.clear();
  161. for (auto& index : queriesIndicesToShutdown)
  162. {
  163. queries[index]->Shutdown();
  164. queriesToShutdown.push_back(queries[index].get());
  165. }
  166. EXPECT_EQ(queryPool->GetResourceCount(), (numQueries - static_cast<uint32_t>(queriesIndicesToShutdown.size())));
  167. result = queryPool->InitQuery(queriesToShutdown.data(), static_cast<uint32_t>(queriesToShutdown.size()));
  168. // Since we are recreating queries it adds a refcount and invalidates the (non-existing) views.
  169. // We need to ensure to release the refcount and avoid leaks by running the invalidate bus.
  170. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  171. checkSlotsFunc(queriesToInitialize);
  172. }
  173. TEST_F(MultiDeviceQueryTests, TestIntervals)
  174. {
  175. static const uint32_t numQueries = 10;
  176. AZStd::array<RHI::Ptr<RHI::Query>, numQueries> queries;
  177. for (auto& query : queries)
  178. {
  179. query = aznew RHI::Query;
  180. }
  181. RHI::Ptr<RHI::QueryPool> queryPool;
  182. queryPool = aznew RHI::QueryPool;
  183. RHI::QueryPoolDescriptor queryPoolDesc;
  184. queryPoolDesc.m_queriesCount = numQueries;
  185. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  186. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  187. queryPool->Init(queryPoolDesc);
  188. AZStd::vector<RHI::Query*> queriesToInitialize(numQueries);
  189. for (size_t i = 0; i < queries.size(); ++i)
  190. {
  191. queriesToInitialize[i] = queries[i].get();
  192. }
  193. RHI::ResultCode result = queryPool->InitQuery(queriesToInitialize.data(), static_cast<uint32_t>(queriesToInitialize.size()));
  194. EXPECT_EQ(result, RHI::ResultCode::Success);
  195. uint64_t results[numQueries * DeviceCount] = {};
  196. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  197. {
  198. auto* testQueryPool = static_cast<UnitTest::QueryPool*>(queryPool->GetDeviceQueryPool(deviceIndex).get());
  199. auto& testQueryPoolIntervals = testQueryPool->m_calledIntervals;
  200. testQueryPoolIntervals.clear();
  201. EXPECT_EQ(queryPool->GetResults(results, numQueries * DeviceCount, RHI::QueryResultFlagBits::None), RHI::ResultCode::Success);
  202. EXPECT_EQ(testQueryPoolIntervals.size(), 1);
  203. EXPECT_EQ(testQueryPoolIntervals.front(), RHI::Interval(0, numQueries - 1));
  204. testQueryPoolIntervals.clear();
  205. auto* queryToTest = queries[5].get();
  206. EXPECT_EQ(queryPool->GetResults(queryToTest, results, DeviceCount, RHI::QueryResultFlagBits::None), RHI::ResultCode::Success);
  207. EXPECT_EQ(testQueryPoolIntervals.size(), 1);
  208. EXPECT_EQ(
  209. testQueryPoolIntervals.front(),
  210. RHI::Interval(queryToTest->GetHandle(deviceIndex).GetIndex(), queryToTest->GetHandle(deviceIndex).GetIndex()));
  211. AZStd::vector<RHI::Interval> intervalsToTest = { RHI::Interval(5, 5), RHI::Interval(0, 3), RHI::Interval(8, 9) };
  212. AZStd::vector<RHI::Query*> queriesToTest;
  213. for (auto& interval : intervalsToTest)
  214. {
  215. for (uint32_t i = interval.m_min; i <= interval.m_max; ++i)
  216. {
  217. queriesToTest.push_back(queries[i].get());
  218. }
  219. }
  220. testQueryPoolIntervals.clear();
  221. EXPECT_EQ(
  222. queryPool->GetResults(
  223. queriesToTest.data(), static_cast<uint32_t>(queriesToTest.size()), results, numQueries * DeviceCount, RHI::QueryResultFlagBits::None),
  224. RHI::ResultCode::Success);
  225. EXPECT_EQ(testQueryPoolIntervals.size(), intervalsToTest.size());
  226. for (auto& interval : intervalsToTest)
  227. {
  228. auto foundIt = AZStd::find(testQueryPoolIntervals.begin(), testQueryPoolIntervals.end(), interval);
  229. EXPECT_NE(foundIt, testQueryPoolIntervals.end());
  230. }
  231. }
  232. }
  233. TEST_F(MultiDeviceQueryTests, TestQuery)
  234. {
  235. AZStd::array<RHI::Ptr<RHI::QueryPool>, RHI::QueryTypeCount> queryPools;
  236. for (size_t i = 0; i < queryPools.size(); ++i)
  237. {
  238. auto& queryPool = queryPools[i];
  239. queryPool = aznew RHI::QueryPool;
  240. RHI::QueryPoolDescriptor queryPoolDesc;
  241. queryPoolDesc.m_queriesCount = 1;
  242. queryPoolDesc.m_type = static_cast<RHI::QueryType>(i);
  243. queryPoolDesc.m_pipelineStatisticsMask = queryPoolDesc.m_type == RHI::QueryType::PipelineStatistics
  244. ? RHI::PipelineStatisticsFlags::CInvocations
  245. : RHI::PipelineStatisticsFlags::None;
  246. queryPool->Init(queryPoolDesc);
  247. }
  248. auto& occlusionQueryPool = queryPools[static_cast<uint32_t>(RHI::QueryType::Occlusion)];
  249. auto& timestampQueryPool = queryPools[static_cast<uint32_t>(RHI::QueryType::Timestamp)];
  250. auto& statisticsQueryPool = queryPools[static_cast<uint32_t>(RHI::QueryType::PipelineStatistics)];
  251. uint64_t data;
  252. RHI::CommandList& dummyCommandList = reinterpret_cast<RHI::CommandList&>(data);
  253. // Correct begin and end for occlusion
  254. {
  255. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  256. EXPECT_EQ(occlusionQueryPool->InitQuery(query.get()), RHI::ResultCode::Success);
  257. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  258. {
  259. EXPECT_EQ(query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList), RHI::ResultCode::Success);
  260. EXPECT_EQ(query->GetDeviceQuery(deviceIndex)->End(dummyCommandList), RHI::ResultCode::Success);
  261. }
  262. }
  263. // Double Begin
  264. {
  265. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  266. occlusionQueryPool->InitQuery(query.get());
  267. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  268. {
  269. query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList);
  270. AZ_TEST_START_ASSERTTEST;
  271. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList));
  272. AZ_TEST_STOP_ASSERTTEST(1);
  273. }
  274. }
  275. // End without Begin
  276. {
  277. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  278. occlusionQueryPool->InitQuery(query.get());
  279. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  280. {
  281. AZ_TEST_START_ASSERTTEST;
  282. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->End(dummyCommandList));
  283. AZ_TEST_STOP_ASSERTTEST(1);
  284. }
  285. }
  286. // End with another command list
  287. {
  288. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  289. occlusionQueryPool->InitQuery(query.get());
  290. uint64_t anotherData;
  291. RHI::CommandList& anotherDummyCmdList = reinterpret_cast<RHI::CommandList&>(anotherData);
  292. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  293. {
  294. EXPECT_EQ(RHI::ResultCode::Success, query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList));
  295. AZ_TEST_START_ASSERTTEST;
  296. EXPECT_EQ(RHI::ResultCode::InvalidArgument, query->GetDeviceQuery(deviceIndex)->End(anotherDummyCmdList));
  297. AZ_TEST_STOP_ASSERTTEST(1);
  298. }
  299. }
  300. // Invalid flag
  301. {
  302. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  303. statisticsQueryPool->InitQuery(query.get());
  304. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  305. {
  306. AZ_TEST_START_ASSERTTEST;
  307. EXPECT_EQ(
  308. RHI::ResultCode::InvalidArgument,
  309. query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList, RHI::QueryControlFlags::PreciseOcclusion));
  310. AZ_TEST_STOP_ASSERTTEST(1);
  311. }
  312. }
  313. // Invalid Begin for Timestamp
  314. {
  315. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  316. timestampQueryPool->InitQuery(query.get());
  317. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  318. {
  319. AZ_TEST_START_ASSERTTEST;
  320. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->Begin(dummyCommandList));
  321. AZ_TEST_STOP_ASSERTTEST(1);
  322. }
  323. }
  324. // Invalid End for Timestamp
  325. {
  326. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  327. timestampQueryPool->InitQuery(query.get());
  328. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  329. {
  330. AZ_TEST_START_ASSERTTEST;
  331. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->End(dummyCommandList));
  332. AZ_TEST_STOP_ASSERTTEST(1);
  333. }
  334. }
  335. // Invalid WriteTimestamp
  336. {
  337. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  338. occlusionQueryPool->InitQuery(query.get());
  339. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  340. {
  341. AZ_TEST_START_ASSERTTEST;
  342. EXPECT_EQ(RHI::ResultCode::Fail, query->GetDeviceQuery(deviceIndex)->WriteTimestamp(dummyCommandList));
  343. AZ_TEST_STOP_ASSERTTEST(1);
  344. }
  345. }
  346. // Correct WriteTimestamp
  347. {
  348. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  349. timestampQueryPool->InitQuery(query.get());
  350. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  351. {
  352. EXPECT_EQ(RHI::ResultCode::Success, query->GetDeviceQuery(deviceIndex)->WriteTimestamp(dummyCommandList));
  353. }
  354. }
  355. }
  356. TEST_F(MultiDeviceQueryTests, TestQueryPoolInitialization)
  357. {
  358. RHI::Ptr<RHI::QueryPool> queryPool;
  359. queryPool = aznew RHI::QueryPool;
  360. RHI::QueryPoolDescriptor queryPoolDesc;
  361. queryPoolDesc.m_queriesCount = 0;
  362. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  363. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  364. // Count of 0
  365. AZ_TEST_START_ASSERTTEST;
  366. EXPECT_EQ(queryPool->Init(queryPoolDesc), RHI::ResultCode::InvalidArgument);
  367. AZ_TEST_STOP_ASSERTTEST(1);
  368. // valid m_pipelineStatisticsMask for Occlusion QueryType
  369. queryPoolDesc.m_queriesCount = 1;
  370. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::CInvocations;
  371. EXPECT_EQ(queryPool->Init(queryPoolDesc), RHI::ResultCode::Success);
  372. // invalid m_pipelineStatisticsMask for PipelineStatistics QueryType
  373. queryPoolDesc.m_type = RHI::QueryType::PipelineStatistics;
  374. queryPoolDesc.m_pipelineStatisticsMask = RHI::PipelineStatisticsFlags::None;
  375. AZ_TEST_START_ASSERTTEST;
  376. EXPECT_EQ(queryPool->Init(queryPoolDesc), RHI::ResultCode::InvalidArgument);
  377. AZ_TEST_STOP_ASSERTTEST(1);
  378. }
  379. TEST_F(MultiDeviceQueryTests, TestResults)
  380. {
  381. AZStd::array<RHI::Ptr<RHI::QueryPool>, 2> queryPools;
  382. RHI::PipelineStatisticsFlags mask = RHI::PipelineStatisticsFlags::CInvocations | RHI::PipelineStatisticsFlags::CPrimitives |
  383. RHI::PipelineStatisticsFlags::IAPrimitives;
  384. for (auto& queryPool : queryPools)
  385. {
  386. queryPool = aznew RHI::QueryPool;
  387. RHI::QueryPoolDescriptor queryPoolDesc;
  388. queryPoolDesc.m_queriesCount = 2;
  389. queryPoolDesc.m_type = RHI::QueryType::PipelineStatistics;
  390. queryPoolDesc.m_pipelineStatisticsMask = mask;
  391. EXPECT_EQ(queryPool->Init(queryPoolDesc), RHI::ResultCode::Success);
  392. }
  393. RHI::Ptr<RHI::Query> query = aznew RHI::Query;
  394. uint32_t numPipelineStatistics = RHI::CountBitsSet(static_cast<uint64_t>(mask));
  395. AZStd::vector<uint64_t> results(numPipelineStatistics * 2 * DeviceCount);
  396. // Using uninitialized query
  397. AZ_TEST_START_ASSERTTEST;
  398. EXPECT_EQ(
  399. queryPools[0]->GetResults(results.data(), numPipelineStatistics * 2 * DeviceCount, RHI::QueryResultFlagBits::None),
  400. RHI::ResultCode::InvalidArgument);
  401. AZ_TEST_STOP_ASSERTTEST(3);
  402. // Wrong size for results count.
  403. queryPools[0]->InitQuery(query.get());
  404. AZ_TEST_START_ASSERTTEST;
  405. EXPECT_EQ(queryPools[0]->GetResults(results.data(), DeviceCount, RHI::QueryResultFlagBits::None), RHI::ResultCode::InvalidArgument);
  406. AZ_TEST_STOP_ASSERTTEST(1);
  407. // Using a query from another pool
  408. RHI::Ptr<RHI::Query> anotherQuery = aznew RHI::Query;
  409. queryPools[1]->InitQuery(anotherQuery.get());
  410. AZ_TEST_START_ASSERTTEST;
  411. EXPECT_EQ(
  412. queryPools[0]->GetResults(anotherQuery.get(), results.data(), numPipelineStatistics * DeviceCount, RHI::QueryResultFlagBits::None),
  413. RHI::ResultCode::InvalidArgument);
  414. AZ_TEST_STOP_ASSERTTEST(1);
  415. // Results count is too small
  416. anotherQuery->Shutdown();
  417. queryPools[0]->InitQuery(anotherQuery.get());
  418. RHI::Query* queries[] = { query.get(), anotherQuery.get() };
  419. AZ_TEST_START_ASSERTTEST;
  420. EXPECT_EQ(
  421. queryPools[0]->GetResults(queries, 2, results.data(), numPipelineStatistics * DeviceCount, RHI::QueryResultFlagBits::None),
  422. RHI::ResultCode::InvalidArgument);
  423. AZ_TEST_STOP_ASSERTTEST(1);
  424. // Correct usage
  425. EXPECT_EQ(
  426. queryPools[0]->GetResults(queries, 2, results.data(), numPipelineStatistics * 5 * DeviceCount, RHI::QueryResultFlagBits::None),
  427. RHI::ResultCode::Success);
  428. // Unsorted queries
  429. {
  430. const size_t numQueries = 5;
  431. AZStd::array<RHI::Ptr<AZ::RHI::Query>, numQueries> queries2;
  432. AZStd::vector<uint64_t> results2(numQueries * DeviceCount);
  433. RHI::Ptr<RHI::QueryPool> queryPool = aznew RHI::QueryPool;
  434. RHI::QueryPoolDescriptor queryPoolDesc;
  435. queryPoolDesc.m_queriesCount = 5;
  436. queryPoolDesc.m_type = RHI::QueryType::Occlusion;
  437. EXPECT_EQ(queryPool->Init(queryPoolDesc), RHI::ResultCode::Success);
  438. for (size_t i = 0; i < queries2.size(); ++i)
  439. {
  440. queries2[i] = aznew RHI::Query;
  441. queryPool->InitQuery(queries2[i].get());
  442. }
  443. AZStd::array<RHI::Query*, numQueries> queriesPtr = {
  444. queries2[2].get(), queries2[0].get(), queries2[1].get(), queries2[3].get(), queries2[4].get()
  445. };
  446. EXPECT_EQ(
  447. queryPool->GetResults(queriesPtr.data(), numQueries, results2.data(), numQueries * DeviceCount, RHI::QueryResultFlagBits::None),
  448. RHI::ResultCode::Success);
  449. for (uint32_t i = 0; i < numQueries; ++i)
  450. {
  451. EXPECT_EQ(results2[i], queriesPtr[i]->GetHandle(AZ::RHI::MultiDevice::DefaultDeviceIndex).GetIndex());
  452. }
  453. }
  454. // Since we are switching queryPools for some queries it adds a refcount and invalidates the views.
  455. // We need to ensure the views are fully invalidated in order to release the refcount and avoid leaks.
  456. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  457. }
  458. } // namespace UnitTest