BufferTests.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  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 <AzCore/Debug/Timer.h>
  10. #include <AzCore/std/parallel/conditional_variable.h>
  11. #include <Tests/Device.h>
  12. #include <Tests/Factory.h>
  13. namespace UnitTest
  14. {
  15. using namespace AZ;
  16. class BufferTests
  17. : public RHITestFixture
  18. {
  19. public:
  20. BufferTests()
  21. : RHITestFixture()
  22. {}
  23. void SetUp() override
  24. {
  25. RHITestFixture::SetUp();
  26. m_factory.reset(aznew Factory());
  27. }
  28. void TearDown() override
  29. {
  30. m_factory.reset();
  31. RHITestFixture::TearDown();
  32. }
  33. private:
  34. AZStd::unique_ptr<Factory> m_factory;
  35. };
  36. TEST_F(BufferTests, TestNoop)
  37. {
  38. RHI::Ptr<RHI::DeviceBuffer> noopBuffer;
  39. noopBuffer = RHI::Factory::Get().CreateBuffer();
  40. }
  41. TEST_F(BufferTests, Test)
  42. {
  43. RHI::Ptr<RHI::Device> device = MakeTestDevice();
  44. RHI::Ptr<RHI::DeviceBuffer> bufferA;
  45. bufferA = RHI::Factory::Get().CreateBuffer();
  46. bufferA->SetName(Name("BufferA"));
  47. AZ_TEST_ASSERT(bufferA->GetName().GetStringView() == "BufferA");
  48. AZ_TEST_ASSERT(bufferA->use_count() == 1);
  49. {
  50. RHI::Ptr<RHI::DeviceBufferPool> bufferPool;
  51. bufferPool = RHI::Factory::Get().CreateBufferPool();
  52. AZ_TEST_ASSERT(bufferPool->use_count() == 1);
  53. RHI::Ptr<RHI::DeviceBuffer> bufferB;
  54. bufferB = RHI::Factory::Get().CreateBuffer();
  55. AZ_TEST_ASSERT(bufferB->use_count() == 1);
  56. RHI::BufferPoolDescriptor bufferPoolDesc;
  57. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant;
  58. bufferPool->Init(*device, bufferPoolDesc);
  59. AZStd::vector<uint8_t> testData(32);
  60. for (uint32_t i = 0; i < 32; ++i)
  61. {
  62. testData[i] = (uint8_t)i * 2;
  63. }
  64. AZ_TEST_ASSERT(bufferA->IsInitialized() == false);
  65. AZ_TEST_ASSERT(bufferB->IsInitialized() == false);
  66. RHI::DeviceBufferInitRequest initRequest;
  67. initRequest.m_buffer = bufferA.get();
  68. initRequest.m_descriptor = RHI::BufferDescriptor(RHI::BufferBindFlags::Constant, 32);
  69. initRequest.m_initialData = testData.data();
  70. bufferPool->InitBuffer(initRequest);
  71. RHI::Ptr<RHI::DeviceBufferView> bufferView;
  72. bufferView = bufferA->GetBufferView(RHI::BufferViewDescriptor::CreateRaw(0, 32));
  73. AZ_TEST_ASSERT(bufferView->IsInitialized());
  74. ASSERT_FALSE(bufferView->IsStale());
  75. AZ_TEST_ASSERT(bufferView->IsFullView());
  76. AZ_TEST_ASSERT(bufferA->use_count() == 2);
  77. AZ_TEST_ASSERT(bufferA->IsInitialized());
  78. AZ_TEST_ASSERT(static_cast<Buffer&>(*bufferA).IsMapped() == false);
  79. initRequest.m_buffer = bufferB.get();
  80. initRequest.m_descriptor = RHI::BufferDescriptor(RHI::BufferBindFlags::Constant, 16);
  81. initRequest.m_initialData = testData.data() + 16;
  82. bufferPool->InitBuffer(initRequest);
  83. AZ_TEST_ASSERT(bufferB->IsInitialized());
  84. AZ_TEST_ASSERT(AZStd::equal(testData.begin(), testData.end(), static_cast<Buffer&>(*bufferA).GetData().begin()));
  85. AZ_TEST_ASSERT(AZStd::equal(testData.begin() + 16, testData.end(), static_cast<Buffer&>(*bufferB).GetData().begin()));
  86. AZ_TEST_ASSERT(bufferA->GetPool() == bufferPool.get());
  87. AZ_TEST_ASSERT(bufferB->GetPool() == bufferPool.get());
  88. AZ_TEST_ASSERT(bufferPool->GetResourceCount() == 2);
  89. {
  90. uint32_t bufferIndex = 0;
  91. const RHI::DeviceBuffer* buffers[] =
  92. {
  93. bufferA.get(),
  94. bufferB.get()
  95. };
  96. bufferPool->ForEach<RHI::DeviceBuffer>([&bufferIndex, &buffers]([[maybe_unused]] RHI::DeviceBuffer& buffer)
  97. {
  98. AZ_UNUSED(buffers); // Prevent unused warning in release builds
  99. AZ_Assert(buffers[bufferIndex] == &buffer, "buffers don't match");
  100. bufferIndex++;
  101. });
  102. }
  103. bufferB->Shutdown();
  104. AZ_TEST_ASSERT(bufferB->GetPool() == nullptr);
  105. RHI::Ptr<RHI::DeviceBufferPool> bufferPoolB;
  106. bufferPoolB = RHI::Factory::Get().CreateBufferPool();
  107. bufferPoolB->Init(*device, bufferPoolDesc);
  108. initRequest.m_buffer = bufferB.get();
  109. initRequest.m_descriptor = RHI::BufferDescriptor(RHI::BufferBindFlags::Constant, 16);
  110. initRequest.m_initialData = testData.data() + 16;
  111. bufferPoolB->InitBuffer(initRequest);
  112. AZ_TEST_ASSERT(bufferB->GetPool() == bufferPoolB.get());
  113. //Since we are switching bufferpools for bufferB it adds a refcount and invalidates the views.
  114. //We need this to ensure the views are fully invalidated in order to release the refcount and avoid a leak.
  115. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  116. bufferPoolB->Shutdown();
  117. AZ_TEST_ASSERT(bufferPoolB->GetResourceCount() == 0);
  118. }
  119. AZ_TEST_ASSERT(bufferA->GetPool() == nullptr);
  120. AZ_TEST_ASSERT(bufferA->use_count() == 1);
  121. }
  122. TEST_F(BufferTests, TestViews)
  123. {
  124. RHI::Ptr<RHI::Device> device = MakeTestDevice();
  125. RHI::Ptr<RHI::DeviceBufferView> bufferViewA;
  126. {
  127. RHI::Ptr<RHI::DeviceBufferPool> bufferPool;
  128. bufferPool = RHI::Factory::Get().CreateBufferPool();
  129. RHI::BufferPoolDescriptor bufferPoolDesc;
  130. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant;
  131. bufferPool->Init(*device, bufferPoolDesc);
  132. RHI::Ptr<RHI::DeviceBuffer> buffer;
  133. buffer = RHI::Factory::Get().CreateBuffer();
  134. RHI::DeviceBufferInitRequest initRequest;
  135. initRequest.m_buffer = buffer.get();
  136. initRequest.m_descriptor = RHI::BufferDescriptor(RHI::BufferBindFlags::Constant, 32);
  137. bufferPool->InitBuffer(initRequest);
  138. // Should report initialized and not stale.
  139. bufferViewA = buffer->GetBufferView(RHI::BufferViewDescriptor::CreateRaw(0, 32));
  140. AZ_TEST_ASSERT(bufferViewA->IsInitialized());
  141. AZ_TEST_ASSERT(bufferViewA->IsStale() == false);
  142. // Should report as still initialized and also stale.
  143. buffer->Shutdown();
  144. AZ_TEST_ASSERT(bufferViewA->IsInitialized());
  145. AZ_TEST_ASSERT(bufferViewA->IsStale());
  146. // Should *still* report as stale since resource invalidation events are queued.
  147. bufferPool->InitBuffer(initRequest);
  148. AZ_TEST_ASSERT(bufferViewA->IsInitialized());
  149. AZ_TEST_ASSERT(bufferViewA->IsStale());
  150. // This should re-initialize the views.
  151. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  152. AZ_TEST_ASSERT(bufferViewA->IsInitialized());
  153. AZ_TEST_ASSERT(bufferViewA->IsStale() == false);
  154. // Explicit invalidation should mark it stale.
  155. buffer->InvalidateViews();
  156. AZ_TEST_ASSERT(bufferViewA->IsInitialized());
  157. AZ_TEST_ASSERT(bufferViewA->IsStale());
  158. // This should re-initialize the views.
  159. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  160. AZ_TEST_ASSERT(bufferViewA->IsInitialized());
  161. AZ_TEST_ASSERT(bufferViewA->IsStale() == false);
  162. // Create an uninitialized bufferview and let it go out of scope
  163. RHI::Ptr<RHI::DeviceBufferView> uninitializedBufferViewPtr = RHI::Factory::Get().CreateBufferView();
  164. }
  165. }
  166. struct BufferAndViewBindFlags
  167. {
  168. RHI::BufferBindFlags bufferBindFlags;
  169. RHI::BufferBindFlags viewBindFlags;
  170. };
  171. class BufferBindFlagTests
  172. : public BufferTests
  173. , public ::testing::WithParamInterface <BufferAndViewBindFlags>
  174. {
  175. public:
  176. void SetUp() override
  177. {
  178. BufferTests::SetUp();
  179. m_device = MakeTestDevice();
  180. // Create a pool and buffer with the buffer bind flags from the parameterized test
  181. m_bufferPool = RHI::Factory::Get().CreateBufferPool();
  182. RHI::BufferPoolDescriptor bufferPoolDesc;
  183. bufferPoolDesc.m_bindFlags = GetParam().bufferBindFlags;
  184. m_bufferPool->Init(*m_device, bufferPoolDesc);
  185. m_buffer = RHI::Factory::Get().CreateBuffer();
  186. RHI::DeviceBufferInitRequest initRequest;
  187. initRequest.m_buffer = m_buffer.get();
  188. initRequest.m_descriptor = RHI::BufferDescriptor(GetParam().bufferBindFlags, 32);
  189. m_bufferPool->InitBuffer(initRequest);
  190. }
  191. RHI::Ptr<RHI::Device> m_device;
  192. RHI::Ptr<RHI::DeviceBufferPool> m_bufferPool;
  193. RHI::Ptr<RHI::DeviceBuffer> m_buffer;
  194. RHI::Ptr<RHI::DeviceBufferView> m_bufferView;
  195. };
  196. TEST_P(BufferBindFlagTests, InitView_ViewIsCreated)
  197. {
  198. RHI::BufferViewDescriptor bufferViewDescriptor;
  199. bufferViewDescriptor.m_overrideBindFlags = GetParam().viewBindFlags;
  200. m_bufferView = m_buffer->GetBufferView(bufferViewDescriptor);
  201. EXPECT_EQ(m_bufferView.get()!=nullptr, true);
  202. }
  203. // This test fixture is the same as BufferBindFlagTests, but exists separately so that
  204. // we can instantiate different test cases that are expected to fail
  205. class BufferBindFlagFailureCases
  206. : public BufferBindFlagTests
  207. {
  208. };
  209. TEST_P(BufferBindFlagFailureCases, InitView_ViewIsNotCreated)
  210. {
  211. RHI::BufferViewDescriptor bufferViewDescriptor;
  212. bufferViewDescriptor.m_overrideBindFlags = GetParam().viewBindFlags;
  213. m_bufferView = m_buffer->GetBufferView(bufferViewDescriptor);
  214. EXPECT_EQ(m_bufferView.get()==nullptr, true);
  215. }
  216. // These combinations should result in a successful creation of the buffer view
  217. std::vector<BufferAndViewBindFlags> GenerateCompatibleBufferBindFlagCombinations()
  218. {
  219. std::vector<BufferAndViewBindFlags> testCases;
  220. BufferAndViewBindFlags flags;
  221. // When the buffer bind flags are equal to or a superset of the buffer view bind flags, the view is compatible with the buffer
  222. flags.bufferBindFlags = RHI::BufferBindFlags::Constant;
  223. flags.viewBindFlags = RHI::BufferBindFlags::Constant;
  224. testCases.push_back(flags);
  225. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  226. flags.viewBindFlags = RHI::BufferBindFlags::ShaderRead;
  227. testCases.push_back(flags);
  228. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  229. flags.viewBindFlags = RHI::BufferBindFlags::ShaderWrite;
  230. testCases.push_back(flags);
  231. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  232. flags.viewBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  233. testCases.push_back(flags);
  234. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderRead;
  235. flags.viewBindFlags = RHI::BufferBindFlags::ShaderRead;
  236. testCases.push_back(flags);
  237. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderWrite;
  238. flags.viewBindFlags = RHI::BufferBindFlags::ShaderWrite;
  239. testCases.push_back(flags);
  240. // When the buffer view bind flags are None, they have no effect and should work with any bind flag used by the buffer
  241. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderRead;
  242. flags.viewBindFlags = RHI::BufferBindFlags::None;
  243. testCases.push_back(flags);
  244. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderWrite;
  245. flags.viewBindFlags = RHI::BufferBindFlags::None;
  246. testCases.push_back(flags);
  247. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  248. flags.viewBindFlags = RHI::BufferBindFlags::None;
  249. testCases.push_back(flags);
  250. flags.bufferBindFlags = RHI::BufferBindFlags::None;
  251. flags.viewBindFlags = RHI::BufferBindFlags::None;
  252. testCases.push_back(flags);
  253. flags.bufferBindFlags = RHI::BufferBindFlags::Constant;
  254. flags.viewBindFlags = RHI::BufferBindFlags::None;
  255. testCases.push_back(flags);
  256. return testCases;
  257. };
  258. // These combinations should fail during BufferView::Init
  259. std::vector<BufferAndViewBindFlags> GenerateIncompatibleBufferBindFlagCombinations()
  260. {
  261. std::vector<BufferAndViewBindFlags> testCases;
  262. BufferAndViewBindFlags flags;
  263. flags.bufferBindFlags = RHI::BufferBindFlags::Constant;
  264. flags.viewBindFlags = RHI::BufferBindFlags::ShaderRead;
  265. testCases.push_back(flags);
  266. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderRead;
  267. flags.viewBindFlags = RHI::BufferBindFlags::ShaderWrite;
  268. testCases.push_back(flags);
  269. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderRead;
  270. flags.viewBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  271. testCases.push_back(flags);
  272. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderWrite;
  273. flags.viewBindFlags = RHI::BufferBindFlags::ShaderRead;
  274. testCases.push_back(flags);
  275. flags.bufferBindFlags = RHI::BufferBindFlags::ShaderWrite;
  276. flags.viewBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  277. testCases.push_back(flags);
  278. flags.bufferBindFlags = RHI::BufferBindFlags::None;
  279. flags.viewBindFlags = RHI::BufferBindFlags::ShaderRead;
  280. testCases.push_back(flags);
  281. flags.bufferBindFlags = RHI::BufferBindFlags::None;
  282. flags.viewBindFlags = RHI::BufferBindFlags::ShaderWrite;
  283. testCases.push_back(flags);
  284. flags.bufferBindFlags = RHI::BufferBindFlags::None;
  285. flags.viewBindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  286. testCases.push_back(flags);
  287. return testCases;
  288. }
  289. std::string BufferBindFlagsToString(RHI::BufferBindFlags bindFlags)
  290. {
  291. switch (bindFlags)
  292. {
  293. case RHI::BufferBindFlags::None:
  294. return "None";
  295. case RHI::BufferBindFlags::Constant:
  296. return "Constant";
  297. case RHI::BufferBindFlags::ShaderRead:
  298. return "ShaderRead";
  299. case RHI::BufferBindFlags::ShaderWrite:
  300. return "ShaderWrite";
  301. case RHI::BufferBindFlags::ShaderReadWrite:
  302. return "ShaderReadWrite";
  303. default:
  304. AZ_Assert(false, "No string conversion was created for this bind flag setting.")
  305. break;
  306. }
  307. return "";
  308. }
  309. std::string GenerateBufferBindFlagTestCaseName(const ::testing::TestParamInfo<BufferAndViewBindFlags>& info)
  310. {
  311. return BufferBindFlagsToString(info.param.bufferBindFlags) + "BufferWith" + BufferBindFlagsToString(info.param.viewBindFlags) + "BufferView";
  312. }
  313. INSTANTIATE_TEST_CASE_P(BufferView, BufferBindFlagTests, ::testing::ValuesIn(GenerateCompatibleBufferBindFlagCombinations()), GenerateBufferBindFlagTestCaseName);
  314. INSTANTIATE_TEST_CASE_P(BufferView, BufferBindFlagFailureCases, ::testing::ValuesIn(GenerateIncompatibleBufferBindFlagCombinations()), GenerateBufferBindFlagTestCaseName);
  315. enum class ParallelGetBufferViewTestCases
  316. {
  317. Get,
  318. GetAndDeferRemoval,
  319. GetCreateAndDeferRemoval
  320. };
  321. enum class ParrallelGetBufferViewCurrentAction
  322. {
  323. Get,
  324. Create,
  325. DeferredRemoval
  326. };
  327. ParrallelGetBufferViewCurrentAction ParallelBufferViewGetCurrentAction(const ParallelGetBufferViewTestCases& testCase)
  328. {
  329. switch (testCase)
  330. {
  331. case ParallelGetBufferViewTestCases::GetAndDeferRemoval:
  332. switch (rand() % 2)
  333. {
  334. case 0:
  335. return ParrallelGetBufferViewCurrentAction::Get;
  336. case 1:
  337. return ParrallelGetBufferViewCurrentAction::DeferredRemoval;
  338. }
  339. case ParallelGetBufferViewTestCases::GetCreateAndDeferRemoval:
  340. switch (rand() % 3)
  341. {
  342. case 0:
  343. return ParrallelGetBufferViewCurrentAction::Get;
  344. case 1:
  345. return ParrallelGetBufferViewCurrentAction::Create;
  346. case 2:
  347. return ParrallelGetBufferViewCurrentAction::DeferredRemoval;
  348. }
  349. case ParallelGetBufferViewTestCases::Get:
  350. default:
  351. return ParrallelGetBufferViewCurrentAction::Get;
  352. }
  353. }
  354. void ParallelGetBufferViewHelper(
  355. const size_t& threadCountMax,
  356. const uint32_t& bufferViewCount,
  357. const uint32_t& iterations,
  358. const ParallelGetBufferViewTestCases& testCase)
  359. {
  360. // printf("Testing threads=%zu assetIds=%zu ... ", threadCountMax, assetIdCount);
  361. AZ::Debug::Timer timer;
  362. timer.Stamp();
  363. // Create the buffer
  364. RHI::Ptr<RHI::Device> device = MakeTestDevice();
  365. constexpr uint32_t viewSize = 32;
  366. constexpr uint32_t maxBufferViewCount = 100;
  367. constexpr uint32_t bufferSize = viewSize * maxBufferViewCount;
  368. AZ_Assert(
  369. maxBufferViewCount >= bufferViewCount,
  370. "This test uses offsets/sizes to create unique BufferViewDescriptors. Ensure the buffer size is large enough to handle the "
  371. "number of unique buffer views.");
  372. RHI::Ptr<RHI::DeviceBufferPool> bufferPool;
  373. bufferPool = RHI::Factory::Get().CreateBufferPool();
  374. RHI::BufferPoolDescriptor bufferPoolDesc;
  375. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant;
  376. bufferPool->Init(*device, bufferPoolDesc);
  377. RHI::Ptr<RHI::DeviceBuffer> buffer;
  378. buffer = RHI::Factory::Get().CreateBuffer();
  379. RHI::DeviceBufferInitRequest initRequest;
  380. initRequest.m_buffer = buffer.get();
  381. initRequest.m_descriptor = RHI::BufferDescriptor(RHI::BufferBindFlags::Constant, bufferSize);
  382. bufferPool->InitBuffer(initRequest);
  383. AZStd::vector<RHI::BufferViewDescriptor> viewDescriptors;
  384. viewDescriptors.reserve(bufferViewCount);
  385. for (uint32_t i = 0; i < bufferViewCount; ++i)
  386. {
  387. viewDescriptors.push_back(RHI::BufferViewDescriptor::CreateRaw(i * viewSize, viewSize));
  388. }
  389. AZStd::vector<AZStd::vector<RHI::Ptr<RHI::DeviceBufferView>>> referenceTable(bufferViewCount);
  390. AZStd::vector<AZStd::thread> threads;
  391. AZStd::mutex mutex;
  392. AZStd::mutex referenceTableMutex;
  393. AZStd::atomic<int> threadCount((int)threadCountMax);
  394. AZStd::condition_variable cv;
  395. for (size_t i = 0; i < threadCountMax; ++i)
  396. {
  397. threads.emplace_back(
  398. [&threadCount, &cv, &buffer, &viewDescriptors, &referenceTable, &iterations, &testCase, &referenceTableMutex]()
  399. {
  400. bool deferRemoval = testCase == ParallelGetBufferViewTestCases::GetAndDeferRemoval ||
  401. testCase == ParallelGetBufferViewTestCases::GetCreateAndDeferRemoval;
  402. for (uint32_t i = 0; i < iterations; ++i) // queue up a bunch of work
  403. {
  404. // Pick a random buffer view to deal with
  405. const size_t index = rand() % viewDescriptors.size();
  406. const RHI::BufferViewDescriptor& viewDescriptor = viewDescriptors[index];
  407. ParrallelGetBufferViewCurrentAction currentAction = ParallelBufferViewGetCurrentAction(testCase);
  408. if (currentAction == ParrallelGetBufferViewCurrentAction::Get ||
  409. currentAction == ParrallelGetBufferViewCurrentAction::Create)
  410. {
  411. RHI::Ptr<RHI::DeviceBufferView> ptr = nullptr;
  412. if (currentAction == ParrallelGetBufferViewCurrentAction::Get)
  413. {
  414. ptr = buffer->GetBufferView(viewDescriptor);
  415. EXPECT_EQ(ptr->GetDescriptor(), viewDescriptor);
  416. }
  417. else if (currentAction == ParrallelGetBufferViewCurrentAction::Create)
  418. {
  419. ptr = RHI::Factory::Get().CreateBufferView();
  420. // Only initialize half of the created references to validated
  421. // that uninitialized views are also threadsafe
  422. if (rand() % 2)
  423. {
  424. RHI::ResultCode resultCode = ptr->Init(static_cast<const Buffer&>(*buffer), viewDescriptor);
  425. EXPECT_EQ(resultCode, RHI::ResultCode::Success);
  426. EXPECT_EQ(ptr->GetDescriptor(), viewDescriptor);
  427. }
  428. }
  429. // Validate the new reference
  430. EXPECT_NE(ptr, nullptr);
  431. if (deferRemoval)
  432. {
  433. // If this test case includes deferring the removal,
  434. // keep a reference to the instance alive so it can be removed later
  435. referenceTableMutex.lock();
  436. referenceTable[index].push_back(ptr);
  437. referenceTableMutex.unlock();
  438. }
  439. }
  440. else if (currentAction == ParrallelGetBufferViewCurrentAction::DeferredRemoval)
  441. {
  442. // Drop the refcount to zero so the instance will be released
  443. referenceTableMutex.lock();
  444. referenceTable[index].clear();
  445. referenceTableMutex.unlock();
  446. }
  447. }
  448. threadCount--;
  449. cv.notify_one();
  450. });
  451. }
  452. bool timedOut = false;
  453. // Used to detect a deadlock. If we wait for more than 10 seconds, it's likely a deadlock has occurred
  454. while (threadCount > 0 && !timedOut)
  455. {
  456. AZStd::unique_lock<AZStd::mutex> lock(mutex);
  457. timedOut =
  458. (AZStd::cv_status::timeout == cv.wait_until(lock, AZStd::chrono::steady_clock::now() + AZStd::chrono::seconds(1)));
  459. }
  460. EXPECT_TRUE(threadCount == 0) << "One or more threads appear to be deadlocked at " << timer.GetDeltaTimeInSeconds()
  461. << " seconds";
  462. for (auto& thread : threads)
  463. {
  464. thread.join();
  465. }
  466. // printf("Took %f seconds\n", timer.GetDeltaTimeInSeconds());
  467. }
  468. void ParallelGetBufferViewTest(const ParallelGetBufferViewTestCases& testCase)
  469. {
  470. // This is the original test scenario from when InstanceDatabase was first implemented
  471. // threads, bufferViews, seconds
  472. ParallelGetBufferViewHelper(8, 100, 5, testCase);
  473. // This value is checked in as 1 so this test doesn't take too much time, but can be increased locally to soak the test.
  474. const size_t attempts = 1;
  475. for (size_t i = 0; i < attempts; ++i)
  476. {
  477. // printf("Attempt %zu of %zu... \n", i, attempts);
  478. // The idea behind this series of tests is that there are two threads sharing one bufferView, and both threads try to
  479. // create or release that view at the same time.
  480. const uint32_t iterations = 1000;
  481. // threads, AssetIds, iterations
  482. ParallelGetBufferViewHelper(2, 1, iterations, testCase);
  483. ParallelGetBufferViewHelper(4, 1, iterations, testCase);
  484. ParallelGetBufferViewHelper(8, 1, iterations, testCase);
  485. // printf("Attempt %zu of %zu... \n", i, attempts);
  486. // Here we try a bunch of different threadCount:bufferViewCount ratios to be thorough
  487. // threads, views, iterations
  488. ParallelGetBufferViewHelper(2, 1, iterations, testCase);
  489. ParallelGetBufferViewHelper(4, 1, iterations, testCase);
  490. ParallelGetBufferViewHelper(4, 2, iterations, testCase);
  491. ParallelGetBufferViewHelper(4, 4, iterations, testCase);
  492. ParallelGetBufferViewHelper(8, 1, iterations, testCase);
  493. ParallelGetBufferViewHelper(8, 2, iterations, testCase);
  494. ParallelGetBufferViewHelper(8, 3, iterations, testCase);
  495. ParallelGetBufferViewHelper(8, 4, iterations, testCase);
  496. }
  497. }
  498. TEST_F(BufferTests, DISABLED_ParallelGetBufferViewTests_Get)
  499. {
  500. ParallelGetBufferViewTest(ParallelGetBufferViewTestCases::Get);
  501. }
  502. TEST_F(BufferTests, DISABLED_ParallelGetBufferViewTests_GetAndDeferRemoval)
  503. {
  504. ParallelGetBufferViewTest(ParallelGetBufferViewTestCases::GetAndDeferRemoval);
  505. }
  506. TEST_F(BufferTests, DISABLED_ParallelGetBufferViewTests_GetCreateAndDeferRemoval)
  507. {
  508. ParallelGetBufferViewTest(ParallelGetBufferViewTestCases::GetCreateAndDeferRemoval);
  509. }
  510. } // namespace UnitTest