MultiDeviceIndirectBufferTests.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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/BufferPool.h>
  11. #include <Atom/RHI/IndirectBufferSignature.h>
  12. #include <Atom/RHI/IndirectBufferWriter.h>
  13. #include <Atom/RHI/StreamBufferView.h>
  14. #include <Atom/RHI/IndexBufferView.h>
  15. #include <Tests/Buffer.h>
  16. #include <Tests/Device.h>
  17. #include <Tests/IndirectBuffer.h>
  18. #include <Atom/RHI.Reflect/ReflectSystemComponent.h>
  19. #include <AzCore/Serialization/ObjectStream.h>
  20. #include <AzCore/Serialization/Utils.h>
  21. namespace UnitTest
  22. {
  23. using namespace AZ;
  24. class MultiDeviceIndirectBufferTests : public MultiDeviceRHITestFixture
  25. {
  26. public:
  27. MultiDeviceIndirectBufferTests()
  28. : MultiDeviceRHITestFixture()
  29. {
  30. }
  31. ~MultiDeviceIndirectBufferTests()
  32. {
  33. }
  34. private:
  35. void SetUp() override
  36. {
  37. MultiDeviceRHITestFixture::SetUp();
  38. m_serializeContext = AZStd::make_unique<SerializeContext>();
  39. RHI::ReflectSystemComponent::Reflect(m_serializeContext.get());
  40. AZ::Name::Reflect(m_serializeContext.get());
  41. m_commands.clear();
  42. m_commands.push_back(RHI::IndirectCommandType::RootConstants);
  43. m_commands.push_back(RHI::IndirectBufferViewArguments{ s_vertexSlotIndex });
  44. m_commands.push_back(RHI::IndirectCommandType::IndexBufferView);
  45. m_commands.push_back(RHI::IndirectCommandType::DrawIndexed);
  46. m_bufferPool = aznew AZ::RHI::BufferPool;
  47. RHI::BufferPoolDescriptor poolDesc;
  48. poolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite;
  49. m_bufferPool->Init(DeviceMask, poolDesc);
  50. m_buffer = aznew AZ::RHI::Buffer;
  51. RHI::BufferInitRequest initRequest;
  52. initRequest.m_buffer = m_buffer.get();
  53. initRequest.m_descriptor.m_byteCount = m_writerCommandStride * m_writerNumCommands;
  54. initRequest.m_descriptor.m_bindFlags = poolDesc.m_bindFlags;
  55. m_bufferPool->InitBuffer(initRequest);
  56. m_writerSignature = CreateInitializedSignature();
  57. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  58. {
  59. EXPECT_CALL(
  60. *static_cast<IndirectBufferSignature*>(m_writerSignature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  61. GetByteStrideInternal())
  62. .WillRepeatedly(testing::Return(m_writerCommandStride));
  63. }
  64. }
  65. void TearDown() override
  66. {
  67. m_buffer.reset();
  68. m_bufferPool.reset();
  69. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  70. {
  71. EXPECT_CALL(
  72. *static_cast<IndirectBufferSignature*>(m_writerSignature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  73. ShutdownInternal())
  74. .Times(1);
  75. }
  76. m_writerSignature.reset();
  77. m_serializeContext.reset();
  78. MultiDeviceRHITestFixture::TearDown();
  79. }
  80. protected:
  81. RHI::IndirectBufferLayout CreateUnfinalizedLayout()
  82. {
  83. RHI::IndirectBufferLayout layout;
  84. for (const auto& descriptor : m_commands)
  85. {
  86. EXPECT_TRUE(layout.AddIndirectCommand(descriptor));
  87. }
  88. return layout;
  89. }
  90. RHI::IndirectBufferLayout CreateFinalizedLayout()
  91. {
  92. auto layout = CreateUnfinalizedLayout();
  93. EXPECT_TRUE(layout.Finalize());
  94. return layout;
  95. }
  96. RHI::IndirectBufferLayout CreateSerializedLayout(const RHI::IndirectBufferLayout& layout)
  97. {
  98. AZStd::vector<char, AZ::OSStdAllocator> buffer;
  99. AZ::IO::ByteContainerStream<AZStd::vector<char, AZ::OSStdAllocator>> outStream(&buffer);
  100. {
  101. AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&outStream, *m_serializeContext.get(), AZ::ObjectStream::ST_BINARY);
  102. bool writeOK = objStream->WriteClass(&layout);
  103. EXPECT_TRUE(writeOK);
  104. bool finalizeOK = objStream->Finalize();
  105. EXPECT_TRUE(finalizeOK);
  106. }
  107. outStream.Seek(0, IO::GenericStream::ST_SEEK_BEGIN);
  108. AZ::ObjectStream::FilterDescriptor filterDesc;
  109. RHI::IndirectBufferLayout deserializedLayout;
  110. bool deserializedOK = AZ::Utils::LoadObjectFromStreamInPlace<RHI::IndirectBufferLayout>(
  111. outStream, deserializedLayout, m_serializeContext.get(), filterDesc);
  112. EXPECT_TRUE(deserializedOK);
  113. return deserializedLayout;
  114. }
  115. void ValidateLayout(const RHI::IndirectBufferLayout& layout)
  116. {
  117. EXPECT_TRUE(layout.IsFinalized());
  118. auto layoutCommands = layout.GetCommands();
  119. EXPECT_EQ(m_commands.size(), layoutCommands.size());
  120. for (uint32_t i = 0; i < m_commands.size(); ++i)
  121. {
  122. EXPECT_EQ(m_commands[i], layoutCommands[i]);
  123. EXPECT_EQ(layout.FindCommandIndex(m_commands[i]), RHI::IndirectCommandIndex(i));
  124. }
  125. }
  126. RHI::Ptr<AZ::RHI::IndirectBufferSignature> CreateInitializedSignature()
  127. {
  128. using namespace ::testing;
  129. auto signature = aznew AZ::RHI::IndirectBufferSignature;
  130. m_signatureDescriptor.m_layout = CreateFinalizedLayout();
  131. EXPECT_EQ(signature->Init(DeviceMask, m_signatureDescriptor), RHI::ResultCode::Success);
  132. return signature;
  133. }
  134. RHI::Ptr<AZ::RHI::IndirectBufferSignature> CreateUnInitializedSignature()
  135. {
  136. auto signature = aznew AZ::RHI::IndirectBufferSignature;
  137. return signature;
  138. }
  139. RHI::Ptr<AZ::RHI::IndirectBufferWriter> CreateInitializedWriter()
  140. {
  141. auto writer = aznew AZ::RHI::IndirectBufferWriter;
  142. EXPECT_EQ(
  143. writer->Init(*m_buffer, m_writerOffset, m_writerCommandStride, m_writerNumCommands, *m_writerSignature),
  144. RHI::ResultCode::Success);
  145. return writer;
  146. }
  147. void ValidateSignature(const RHI::IndirectBufferSignature& signature)
  148. {
  149. ValidateLayout(signature.GetLayout());
  150. EXPECT_TRUE(signature.IsInitialized());
  151. }
  152. void ValidateWriter(const AZ::RHI::IndirectBufferWriter& writer)
  153. {
  154. auto currentSequenceIndex{ writer.GetCurrentSequenceIndex() };
  155. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  156. {
  157. EXPECT_EQ(
  158. static_cast<IndirectBufferWriter*>(writer.GetDeviceIndirectBufferWriter(deviceIndex).get())->GetData(),
  159. static_cast<const uint8_t*>(static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->GetData().data()));
  160. EXPECT_EQ(currentSequenceIndex, 0);
  161. EXPECT_TRUE(static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->IsMapped());
  162. }
  163. }
  164. static const uint32_t s_vertexSlotIndex = 3;
  165. AZStd::vector<RHI::IndirectCommandDescriptor> m_commands;
  166. AZStd::unique_ptr<SerializeContext> m_serializeContext;
  167. RHI::IndirectBufferSignatureDescriptor m_signatureDescriptor;
  168. RHI::Ptr<AZ::RHI::BufferPool> m_bufferPool;
  169. RHI::Ptr<AZ::RHI::Buffer> m_buffer;
  170. size_t m_writerOffset = 0;
  171. uint32_t m_writerCommandStride = 2;
  172. uint32_t m_writerNumCommands = 1024;
  173. RHI::Ptr<AZ::RHI::IndirectBufferSignature> m_writerSignature;
  174. };
  175. TEST_F(MultiDeviceIndirectBufferTests, TestSignature)
  176. {
  177. // Normal initialization
  178. {
  179. auto signature = CreateInitializedSignature();
  180. EXPECT_TRUE(signature != nullptr);
  181. ValidateSignature(*signature);
  182. }
  183. // GetByteStride() on uninitialized signature.
  184. {
  185. auto signature = CreateUnInitializedSignature();
  186. AZ_TEST_START_TRACE_SUPPRESSION;
  187. signature->GetByteStride();
  188. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  189. }
  190. // GetOffset()
  191. {
  192. auto signature = CreateInitializedSignature();
  193. uint32_t offset = 1337;
  194. RHI::IndirectCommandIndex index(m_commands.size() - 1);
  195. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  196. {
  197. EXPECT_CALL(
  198. *static_cast<IndirectBufferSignature*>(signature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  199. GetOffsetInternal(index))
  200. .Times(1)
  201. .WillOnce(testing::Return(offset));
  202. }
  203. EXPECT_EQ(signature->GetOffset(index), offset);
  204. }
  205. // GetOffset with null index
  206. {
  207. auto signature = CreateInitializedSignature();
  208. RHI::IndirectCommandIndex index = RHI::IndirectCommandIndex::Null;
  209. AZ_TEST_START_TRACE_SUPPRESSION;
  210. signature->GetOffset(index);
  211. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  212. }
  213. // GetOffset with invalid index
  214. {
  215. auto signature = CreateInitializedSignature();
  216. RHI::IndirectCommandIndex index(m_commands.size());
  217. AZ_TEST_START_TRACE_SUPPRESSION;
  218. signature->GetOffset(index);
  219. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  220. }
  221. // Shutdown
  222. {
  223. auto signature = CreateInitializedSignature();
  224. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  225. {
  226. EXPECT_CALL(
  227. *static_cast<IndirectBufferSignature*>(signature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  228. ShutdownInternal())
  229. .Times(1);
  230. }
  231. }
  232. }
  233. TEST_F(MultiDeviceIndirectBufferTests, TestWriter)
  234. {
  235. // Normal Initialization
  236. {
  237. auto writer = CreateInitializedWriter();
  238. EXPECT_TRUE(writer != nullptr);
  239. ValidateWriter(*writer);
  240. }
  241. // Initialization with invalid size
  242. {
  243. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  244. AZ_TEST_START_TRACE_SUPPRESSION;
  245. EXPECT_EQ(
  246. writer->Init(*m_buffer, 1, m_writerCommandStride, m_writerNumCommands, *m_writerSignature),
  247. RHI::ResultCode::InvalidArgument);
  248. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  249. }
  250. // Initialization with invalid stride
  251. {
  252. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  253. AZ_TEST_START_TRACE_SUPPRESSION;
  254. EXPECT_EQ(
  255. writer->Init(*m_buffer, m_writerOffset, 0, m_writerNumCommands, *m_writerSignature), RHI::ResultCode::InvalidArgument);
  256. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  257. }
  258. // Initialization with invalid max num sequences
  259. {
  260. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  261. AZ_TEST_START_TRACE_SUPPRESSION;
  262. EXPECT_EQ(
  263. writer->Init(*m_buffer, m_writerOffset, m_writerCommandStride, 0, *m_writerSignature), RHI::ResultCode::InvalidArgument);
  264. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  265. }
  266. // Initialization with small invalid stride
  267. {
  268. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  269. AZ_TEST_START_TRACE_SUPPRESSION;
  270. EXPECT_EQ(
  271. writer->Init(*m_buffer, m_writerOffset, m_writerCommandStride - 1, m_writerNumCommands, *m_writerSignature),
  272. RHI::ResultCode::InvalidArgument);
  273. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  274. }
  275. // Initialization with invalid signature
  276. {
  277. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  278. auto signature = CreateUnInitializedSignature();
  279. AZ_TEST_START_TRACE_SUPPRESSION;
  280. EXPECT_EQ(
  281. writer->Init(*m_buffer, m_writerOffset, m_writerCommandStride, m_writerNumCommands, *signature),
  282. RHI::ResultCode::InvalidArgument);
  283. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  284. }
  285. // Initialization with offset
  286. {
  287. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  288. size_t offset = 16;
  289. EXPECT_EQ(writer->Init(*m_buffer, offset, m_writerCommandStride, 5, *m_writerSignature), RHI::ResultCode::Success);
  290. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  291. {
  292. EXPECT_EQ(
  293. static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get())->GetData(),
  294. static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->GetData().data() + offset);
  295. }
  296. }
  297. // Initialization with memory pointer
  298. {
  299. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  300. AZStd::unordered_map<int, void*> memoryPtrs;
  301. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  302. {
  303. memoryPtrs[deviceIndex] = const_cast<uint8_t*>(
  304. static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->GetData().data());
  305. }
  306. EXPECT_EQ(writer->Init(memoryPtrs, m_writerCommandStride, m_writerNumCommands, *m_writerSignature), RHI::ResultCode::Success);
  307. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  308. {
  309. EXPECT_EQ(
  310. static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get())->GetData(),
  311. static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->GetData().data());
  312. }
  313. }
  314. // Double Init
  315. {
  316. auto writer = CreateInitializedWriter();
  317. AZ_TEST_START_TRACE_SUPPRESSION;
  318. EXPECT_EQ(
  319. writer->Init(*m_buffer, m_writerOffset, m_writerCommandStride, m_writerNumCommands, *m_writerSignature),
  320. RHI::ResultCode::InvalidOperation);
  321. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  322. }
  323. // Valid Seek
  324. {
  325. auto writer = CreateInitializedWriter();
  326. uint32_t seekPos = 2;
  327. EXPECT_TRUE(writer->Seek(seekPos));
  328. {
  329. auto currentSequenceIndex{ writer->GetCurrentSequenceIndex() };
  330. EXPECT_EQ(currentSequenceIndex, seekPos);
  331. }
  332. seekPos += 6;
  333. EXPECT_TRUE(writer->Seek(seekPos));
  334. {
  335. auto currentSequenceIndex{ writer->GetCurrentSequenceIndex() };
  336. EXPECT_EQ(currentSequenceIndex, seekPos);
  337. }
  338. }
  339. // Invalid Seek
  340. {
  341. auto writer = CreateInitializedWriter();
  342. EXPECT_FALSE(writer->Seek(m_writerNumCommands + 1));
  343. {
  344. auto currentSequenceIndex{ writer->GetCurrentSequenceIndex() };
  345. EXPECT_EQ(currentSequenceIndex, 0);
  346. }
  347. }
  348. // Valid NextSequence
  349. {
  350. auto writer = CreateInitializedWriter();
  351. EXPECT_TRUE(writer->NextSequence());
  352. {
  353. auto currentSequenceIndex{ writer->GetCurrentSequenceIndex() };
  354. EXPECT_EQ(currentSequenceIndex, 1);
  355. }
  356. }
  357. // Invalid NextSequence
  358. {
  359. auto writer = CreateInitializedWriter();
  360. EXPECT_TRUE(writer->Seek(m_writerNumCommands - 1));
  361. EXPECT_FALSE(writer->NextSequence());
  362. {
  363. auto currentSequenceIndex{ writer->GetCurrentSequenceIndex() };
  364. EXPECT_EQ(currentSequenceIndex, m_writerNumCommands - 1);
  365. }
  366. }
  367. // Valid Command
  368. {
  369. auto writer = CreateInitializedWriter();
  370. for (const auto& command : m_commands)
  371. {
  372. switch (command.m_type)
  373. {
  374. case RHI::IndirectCommandType::VertexBufferView:
  375. {
  376. auto index = m_signatureDescriptor.m_layout.FindCommandIndex(RHI::IndirectBufferViewArguments{ s_vertexSlotIndex });
  377. EXPECT_FALSE(index.IsNull());
  378. AZ::RHI::StreamBufferView bufferView(*m_buffer, 0, 12, 10);
  379. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  380. EXPECT_CALL(
  381. *static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get()),
  382. SetVertexViewInternal(index, testing::_))
  383. .Times(1);
  384. writer->SetVertexView(s_vertexSlotIndex, bufferView);
  385. break;
  386. }
  387. case RHI::IndirectCommandType::IndexBufferView:
  388. {
  389. auto index = m_signatureDescriptor.m_layout.FindCommandIndex(command.m_type);
  390. EXPECT_FALSE(index.IsNull());
  391. AZ::RHI::IndexBufferView indexView(*m_buffer, 0, 12, RHI::IndexFormat::Uint16);
  392. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  393. EXPECT_CALL(
  394. *static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get()),
  395. SetIndexViewInternal(index, testing::_))
  396. .Times(1);
  397. writer->SetIndexView(indexView);
  398. break;
  399. }
  400. case RHI::IndirectCommandType::DrawIndexed:
  401. {
  402. auto index = m_signatureDescriptor.m_layout.FindCommandIndex(command.m_type);
  403. EXPECT_FALSE(index.IsNull());
  404. AZ::RHI::DrawIndexed arguments(1, 2, 3, 4, 5);
  405. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  406. EXPECT_CALL(
  407. *static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get()),
  408. DrawIndexedInternal(index, testing::_))
  409. .Times(1);
  410. writer->DrawIndexed(arguments);
  411. break;
  412. }
  413. case RHI::IndirectCommandType::RootConstants:
  414. {
  415. auto index = m_signatureDescriptor.m_layout.FindCommandIndex(command.m_type);
  416. EXPECT_FALSE(index.IsNull());
  417. size_t rootConstant;
  418. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  419. EXPECT_CALL(
  420. *static_cast<IndirectBufferSignature*>(
  421. m_writerSignature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  422. GetOffsetInternal(index))
  423. .Times(1)
  424. .WillOnce(testing::Return(0));
  425. auto nextIndex = RHI::IndirectCommandIndex(index.GetIndex() + 1);
  426. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  427. EXPECT_CALL(
  428. *static_cast<IndirectBufferSignature*>(
  429. m_writerSignature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  430. GetOffsetInternal(nextIndex))
  431. .Times(1)
  432. .WillOnce(testing::Return(static_cast<uint32_t>(sizeof(rootConstant))));
  433. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  434. EXPECT_CALL(
  435. *static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get()),
  436. SetRootConstantsInternal(index, reinterpret_cast<uint8_t*>(&rootConstant), sizeof(rootConstant)))
  437. .Times(1);
  438. writer->SetRootConstants(reinterpret_cast<uint8_t*>(&rootConstant), sizeof(rootConstant));
  439. break;
  440. }
  441. default:
  442. break;
  443. }
  444. }
  445. }
  446. // Invalid command
  447. {
  448. auto writer = CreateInitializedWriter();
  449. RHI::DispatchDirect args;
  450. AZ_TEST_START_TRACE_SUPPRESSION;
  451. writer->Dispatch(args);
  452. AZ_TEST_STOP_TRACE_SUPPRESSION(DeviceCount);
  453. }
  454. // Write command on uninitialized writer
  455. {
  456. RHI::Ptr<AZ::RHI::IndirectBufferWriter> writer = aznew AZ::RHI::IndirectBufferWriter;
  457. ;
  458. AZ::RHI::DrawIndexed arguments(1, 2, 3, 4, 5);
  459. AZ_TEST_START_TRACE_SUPPRESSION;
  460. writer->DrawIndexed(arguments);
  461. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  462. }
  463. // Flush
  464. {
  465. auto writer = CreateInitializedWriter();
  466. writer->Flush();
  467. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  468. EXPECT_FALSE(static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->IsMapped());
  469. AZ::RHI::IndexBufferView indexView(*m_buffer, 0, 12, RHI::IndexFormat::Uint16);
  470. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  471. EXPECT_CALL(
  472. *static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get()),
  473. SetIndexViewInternal(testing::_, testing::_))
  474. .Times(1);
  475. writer->SetIndexView(indexView);
  476. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  477. EXPECT_TRUE(static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->IsMapped());
  478. }
  479. // Inline Constants Command with incorrect size
  480. {
  481. auto writer = CreateInitializedWriter();
  482. auto findIt = AZStd::find_if(
  483. m_commands.begin(),
  484. m_commands.end(),
  485. [](const auto& element)
  486. {
  487. return element.m_type == RHI::IndirectCommandType::RootConstants;
  488. });
  489. EXPECT_NE(findIt, m_commands.end());
  490. auto commandIndex = m_writerSignature->GetLayout().FindCommandIndex(*findIt);
  491. EXPECT_FALSE(commandIndex.IsNull());
  492. auto nextCommandIndex = RHI::IndirectCommandIndex(commandIndex.GetIndex() + 1);
  493. uint32_t commandOffsett = 12;
  494. uint32_t nextCommandOffset = 16;
  495. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  496. {
  497. EXPECT_CALL(
  498. *static_cast<IndirectBufferSignature*>(m_writerSignature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  499. GetOffsetInternal(commandIndex))
  500. .Times(1)
  501. .WillOnce(testing::Return(commandOffsett));
  502. EXPECT_CALL(
  503. *static_cast<IndirectBufferSignature*>(m_writerSignature->GetDeviceIndirectBufferSignature(deviceIndex).get()),
  504. GetOffsetInternal(nextCommandIndex))
  505. .Times(1)
  506. .WillOnce(testing::Return(nextCommandOffset));
  507. }
  508. AZ_TEST_START_TRACE_SUPPRESSION;
  509. uint64_t data;
  510. writer->SetRootConstants(reinterpret_cast<uint8_t*>(&data), sizeof(data));
  511. AZ_TEST_STOP_TRACE_SUPPRESSION(DeviceCount);
  512. }
  513. // Shutdown
  514. {
  515. auto writer = CreateInitializedWriter();
  516. writer->Shutdown();
  517. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  518. EXPECT_FALSE(static_cast<Buffer*>(m_buffer->GetDeviceBuffer(deviceIndex).get())->IsMapped());
  519. for (auto deviceIndex{ 0 }; deviceIndex < DeviceCount; ++deviceIndex)
  520. EXPECT_TRUE(
  521. static_cast<IndirectBufferWriter*>(writer->GetDeviceIndirectBufferWriter(deviceIndex).get())->GetData() == nullptr);
  522. }
  523. }
  524. } // namespace UnitTest