MultiDeviceShaderResourceGroupTests.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  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.Reflect/ReflectSystemComponent.h>
  10. #include <Atom/RHI/Factory.h>
  11. #include <Atom/RHI/ShaderResourceGroup.h>
  12. #include <Atom/RHI/ShaderResourceGroupData.h>
  13. #include <Atom/RHI/ShaderResourceGroupPool.h>
  14. #include <AzCore/Math/Matrix3x3.h>
  15. #include <AzCore/Math/Matrix3x4.h>
  16. #include <AzCore/Math/Matrix4x4.h>
  17. #include <AzCore/Math/Vector2.h>
  18. #include <AzCore/Math/Vector3.h>
  19. #include <AzCore/Math/Vector4.h>
  20. #include <AzCore/Memory/SystemAllocator.h>
  21. #include <AzCore/Serialization/ObjectStream.h>
  22. #include <AzCore/Serialization/Utils.h>
  23. #include <Tests/Device.h>
  24. #include <Tests/ShaderResourceGroup.h>
  25. namespace UnitTest
  26. {
  27. using namespace AZ;
  28. class MultiDeviceShaderResourceGroupTests : public MultiDeviceRHITestFixture
  29. {
  30. private:
  31. struct NestedData
  32. {
  33. float m_x;
  34. float m_y;
  35. float m_z;
  36. };
  37. struct ConstantBufferTest
  38. {
  39. float m_floatValue;
  40. uint32_t m_uintValue[3];
  41. float m_float4Value[4];
  42. NestedData m_nestedData[16];
  43. AZ::Matrix3x3 m_matrix3x3;
  44. AZ::Matrix4x4 m_matrix4x4;
  45. AZ::Matrix3x4 m_matrix3x4;
  46. AZ::Vector2 m_vector2;
  47. AZ::Vector3 m_vector3;
  48. AZ::Vector4 m_vector4;
  49. };
  50. const uint32_t ImageReadCount = 5;
  51. const uint32_t ImageReadWriteCount = 8;
  52. const uint32_t BufferConstantCount = 2;
  53. const uint32_t BufferReadCount = 2;
  54. const uint32_t BufferReadWriteCount = 2;
  55. AZStd::unique_ptr<SerializeContext> m_serializeContext;
  56. public:
  57. void SetUp() override
  58. {
  59. MultiDeviceRHITestFixture::SetUp();
  60. m_serializeContext = AZStd::make_unique<SerializeContext>();
  61. RHI::ReflectSystemComponent::Reflect(m_serializeContext.get());
  62. AZ::Name::Reflect(m_serializeContext.get());
  63. }
  64. void TearDown() override
  65. {
  66. m_serializeContext.reset();
  67. MultiDeviceRHITestFixture::TearDown();
  68. }
  69. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> CreateLayout()
  70. {
  71. RHI::Ptr<RHI::ShaderResourceGroupLayout> layout = RHI::ShaderResourceGroupLayout::Create();
  72. layout->SetBindingSlot(0);
  73. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{
  74. Name("m_floatValue"), offsetof(ConstantBufferTest, m_floatValue), sizeof(ConstantBufferTest::m_floatValue), 0, 0 });
  75. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{
  76. Name("m_uintValue"), offsetof(ConstantBufferTest, m_uintValue), sizeof(ConstantBufferTest::m_uintValue), 0, 0 });
  77. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{
  78. Name("m_float4Value"), offsetof(ConstantBufferTest, m_float4Value), sizeof(ConstantBufferTest::m_float4Value), 0, 0 });
  79. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{
  80. Name("m_nestedData"), offsetof(ConstantBufferTest, m_nestedData), sizeof(ConstantBufferTest::m_nestedData), 0, 0 });
  81. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{
  82. Name("m_matrix3x3"),
  83. offsetof(ConstantBufferTest, m_matrix3x3),
  84. 44,
  85. 0,
  86. 0 }); // Shader packs rows into 4 floats not 3, but doesn't include the last float on the last row, hence 44
  87. layout->AddShaderInput(
  88. RHI::ShaderInputConstantDescriptor{ Name("m_matrix4x4"), offsetof(ConstantBufferTest, m_matrix4x4), 64, 0, 0 });
  89. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{ Name("m_matrix3x4"),
  90. offsetof(ConstantBufferTest, m_matrix3x4),
  91. 48,
  92. 0,
  93. 0 }); // Shader packs rows into 4 floats not 3, hence its 48
  94. layout->AddShaderInput(
  95. RHI::ShaderInputConstantDescriptor{ Name("m_vector2"), offsetof(ConstantBufferTest, m_vector2), 8, 0, 0 });
  96. layout->AddShaderInput(
  97. RHI::ShaderInputConstantDescriptor{ Name("m_vector3"), offsetof(ConstantBufferTest, m_vector3), 12, 0, 0 });
  98. layout->AddShaderInput(
  99. RHI::ShaderInputConstantDescriptor{ Name("m_vector4"), offsetof(ConstantBufferTest, m_vector4), 16, 0, 0 });
  100. layout->AddShaderInput(RHI::ShaderInputImageDescriptor{
  101. Name("m_readImage"), RHI::ShaderInputImageAccess::Read, RHI::ShaderInputImageType::Image2D, ImageReadCount, 1, 1 });
  102. layout->AddShaderInput(RHI::ShaderInputImageDescriptor{ Name("m_readWriteImage"),
  103. RHI::ShaderInputImageAccess::ReadWrite,
  104. RHI::ShaderInputImageType::Image2D,
  105. ImageReadWriteCount,
  106. 2,
  107. 2 });
  108. layout->AddShaderInput(RHI::ShaderInputBufferDescriptor{ Name("m_constantBuffer"),
  109. RHI::ShaderInputBufferAccess::Constant,
  110. RHI::ShaderInputBufferType::Constant,
  111. BufferConstantCount,
  112. UINT_MAX,
  113. 3,
  114. 3 });
  115. layout->AddShaderInput(RHI::ShaderInputBufferDescriptor{ Name("m_readBuffer"),
  116. RHI::ShaderInputBufferAccess::Read,
  117. RHI::ShaderInputBufferType::Structured,
  118. BufferReadCount,
  119. UINT_MAX,
  120. 4,
  121. 4 });
  122. layout->AddShaderInput(RHI::ShaderInputBufferDescriptor{ Name("m_readWriteBuffer"),
  123. RHI::ShaderInputBufferAccess::ReadWrite,
  124. RHI::ShaderInputBufferType::Typed,
  125. BufferReadWriteCount,
  126. UINT_MAX,
  127. 5,
  128. 5 });
  129. layout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{
  130. Name("m_sampler"), RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 6, 6 });
  131. bool success = layout->Finalize();
  132. if (!success)
  133. {
  134. return nullptr;
  135. }
  136. return layout;
  137. }
  138. void CreateSerializedLayout(RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& serializedSrgLayout)
  139. {
  140. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  141. AZStd::vector<char, AZ::OSStdAllocator> srgBuffer;
  142. AZ::IO::ByteContainerStream<AZStd::vector<char, AZ::OSStdAllocator>> outStream(&srgBuffer);
  143. {
  144. AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&outStream, *m_serializeContext.get(), AZ::ObjectStream::ST_BINARY);
  145. bool writeOK = objStream->WriteClass(srgLayout.get());
  146. ASSERT_TRUE(writeOK);
  147. bool finalizeOK = objStream->Finalize();
  148. ASSERT_TRUE(finalizeOK);
  149. }
  150. outStream.Seek(0, IO::GenericStream::ST_SEEK_BEGIN);
  151. AZ::ObjectStream::FilterDescriptor filterDesc;
  152. serializedSrgLayout =
  153. AZ::Utils::LoadObjectFromStream<RHI::ShaderResourceGroupLayout>(outStream, m_serializeContext.get(), filterDesc);
  154. }
  155. void TestShaderResourceGroupLayout()
  156. {
  157. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  158. TestShaderResourceGroupReflection(srgLayout);
  159. }
  160. void TestShaderResourceGroupLayoutSerialized()
  161. {
  162. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout;
  163. CreateSerializedLayout(srgLayout);
  164. TestShaderResourceGroupReflection(srgLayout);
  165. }
  166. void TestShaderResourceGroupPools()
  167. {
  168. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  169. {
  170. RHI::Ptr<RHI::ShaderResourceGroup> srgA = aznew AZ::RHI::ShaderResourceGroup;
  171. {
  172. RHI::Ptr<RHI::ShaderResourceGroupPool> srgPool = aznew AZ::RHI::ShaderResourceGroupPool;
  173. RHI::ShaderResourceGroupPoolDescriptor descriptor;
  174. descriptor.m_budgetInBytes = 16;
  175. descriptor.m_layout = srgLayout.get();
  176. ASSERT_FALSE(srgPool->IsInitialized());
  177. srgPool->Init(descriptor);
  178. ASSERT_TRUE(srgPool->IsInitialized());
  179. srgPool->Shutdown();
  180. ASSERT_FALSE(srgPool->IsInitialized());
  181. srgPool->Init(descriptor);
  182. ASSERT_TRUE(srgPool->IsInitialized());
  183. ASSERT_TRUE(srgPool->use_count() == 1);
  184. ASSERT_TRUE(srgLayout->use_count() == (3 + DeviceCount));
  185. RHI::Ptr<RHI::ShaderResourceGroup> srgB = aznew AZ::RHI::ShaderResourceGroup;
  186. ASSERT_TRUE(srgA->GetPool() == nullptr);
  187. srgPool->InitGroup(*srgA);
  188. ASSERT_TRUE(srgA->IsInitialized());
  189. ASSERT_TRUE(srgA->GetPool() == srgPool.get());
  190. ASSERT_TRUE(srgPool->GetResourceCount() == 1);
  191. srgA->Shutdown();
  192. ASSERT_TRUE(srgPool->GetResourceCount() == 0);
  193. ASSERT_TRUE(srgA->IsInitialized() == false);
  194. ASSERT_TRUE(srgA->GetPool() == nullptr);
  195. srgPool->InitGroup(*srgA);
  196. ASSERT_TRUE(srgA->IsInitialized());
  197. ASSERT_TRUE(srgA->GetPool() == srgPool.get());
  198. srgPool->InitGroup(*srgB);
  199. // Called to flush Resource::InvalidateViews() which has an increment/decrement for the use_count
  200. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  201. ASSERT_TRUE(srgA->use_count() == 1);
  202. ASSERT_TRUE(srgB->use_count() == 1);
  203. ASSERT_TRUE(srgPool->GetResourceCount() == 2);
  204. {
  205. uint32_t srgIndex = 0;
  206. const RHI::ShaderResourceGroup* srgs[] = { srgA.get(), srgB.get() };
  207. srgPool->ForEach<RHI::ShaderResourceGroup>(
  208. [&srgIndex, &srgs](const RHI::ShaderResourceGroup& srg)
  209. {
  210. ASSERT_TRUE(srgs[srgIndex] == &srg);
  211. srgIndex++;
  212. });
  213. }
  214. }
  215. ASSERT_TRUE(srgA->IsInitialized() == false);
  216. ASSERT_TRUE(srgA->GetPool() == nullptr);
  217. }
  218. ASSERT_TRUE(srgLayout->use_count() == 1);
  219. RHI::Ptr<RHI::ShaderResourceGroup> noopShaderResourceGroup = aznew AZ::RHI::ShaderResourceGroup;
  220. }
  221. void TestShaderResourceGroupReflection(const RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& srgLayout)
  222. {
  223. EXPECT_EQ(srgLayout->GetGroupSizeForImages(), ImageReadCount + ImageReadWriteCount);
  224. EXPECT_EQ(srgLayout->GetGroupSizeForBuffers(), BufferConstantCount + BufferReadCount + BufferReadWriteCount);
  225. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputImageIndex(0)).m_min, 0);
  226. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputImageIndex(0)).m_max, ImageReadCount);
  227. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputImageIndex(1)).m_min, ImageReadCount);
  228. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputImageIndex(1)).m_max, ImageReadCount + ImageReadWriteCount);
  229. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputBufferIndex(0)).m_min, 0);
  230. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputBufferIndex(0)).m_max, BufferConstantCount);
  231. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputBufferIndex(1)).m_min, BufferConstantCount);
  232. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputBufferIndex(1)).m_max, BufferConstantCount + BufferReadCount);
  233. EXPECT_EQ(srgLayout->GetGroupInterval(RHI::ShaderInputBufferIndex(2)).m_min, BufferConstantCount + BufferReadCount);
  234. EXPECT_EQ(
  235. srgLayout->GetGroupInterval(RHI::ShaderInputBufferIndex(2)).m_max,
  236. BufferConstantCount + BufferReadCount + BufferReadWriteCount);
  237. EXPECT_EQ(srgLayout->use_count(), 1);
  238. RHI::ShaderInputImageIndex imageInputIndex = srgLayout->FindShaderInputImageIndex(Name("m_readImage"));
  239. EXPECT_EQ(imageInputIndex.GetIndex(), 0);
  240. imageInputIndex = srgLayout->FindShaderInputImageIndex(Name("m_readWriteImage"));
  241. EXPECT_EQ(imageInputIndex.GetIndex(), 1);
  242. RHI::ShaderInputBufferIndex bufferInputIndex = srgLayout->FindShaderInputBufferIndex(Name("m_constantBuffer"));
  243. EXPECT_EQ(bufferInputIndex.GetIndex(), 0);
  244. bufferInputIndex = srgLayout->FindShaderInputBufferIndex(Name("m_readBuffer"));
  245. EXPECT_EQ(bufferInputIndex.GetIndex(), 1);
  246. bufferInputIndex = srgLayout->FindShaderInputBufferIndex(Name("m_readWriteBuffer"));
  247. EXPECT_EQ(bufferInputIndex.GetIndex(), 2);
  248. RHI::ShaderInputConstantIndex floatValueIndex = srgLayout->FindShaderInputConstantIndex(Name("m_floatValue"));
  249. ASSERT_TRUE(floatValueIndex.GetIndex() == 0);
  250. RHI::ShaderInputConstantIndex uintValueIndex = srgLayout->FindShaderInputConstantIndex(Name("m_uintValue"));
  251. ASSERT_TRUE(uintValueIndex.GetIndex() == 1);
  252. RHI::ShaderInputConstantIndex float4ValueIndex = srgLayout->FindShaderInputConstantIndex(Name("m_float4Value"));
  253. ASSERT_TRUE(float4ValueIndex.GetIndex() == 2);
  254. RHI::ShaderInputConstantIndex nestedDataIndex = srgLayout->FindShaderInputConstantIndex(Name("m_nestedData"));
  255. ASSERT_TRUE(nestedDataIndex.GetIndex() == 3);
  256. RHI::ShaderInputConstantIndex matrix3x3Index = srgLayout->FindShaderInputConstantIndex(Name("m_matrix3x3"));
  257. ASSERT_TRUE(matrix3x3Index.GetIndex() == 4);
  258. RHI::ShaderInputConstantIndex matrix4x4Index = srgLayout->FindShaderInputConstantIndex(Name("m_matrix4x4"));
  259. ASSERT_TRUE(matrix4x4Index.GetIndex() == 5);
  260. RHI::ShaderInputConstantIndex matrix3x4Index = srgLayout->FindShaderInputConstantIndex(Name("m_matrix3x4"));
  261. ASSERT_TRUE(matrix3x4Index.GetIndex() == 6);
  262. RHI::Ptr<RHI::ShaderResourceGroupPool> srgPool = aznew AZ::RHI::ShaderResourceGroupPool;
  263. RHI::ShaderResourceGroupPoolDescriptor descriptor;
  264. descriptor.m_budgetInBytes = 16;
  265. descriptor.m_layout = srgLayout.get();
  266. srgPool->Init(descriptor);
  267. RHI::Ptr<RHI::ShaderResourceGroup> srg = aznew AZ::RHI::ShaderResourceGroup;
  268. srgPool->InitGroup(*srg);
  269. RHI::ShaderResourceGroupData srgData(*srg);
  270. float floatValue = 1.234f;
  271. srgData.SetConstant(floatValueIndex, floatValue);
  272. AZStd::array<uint32_t, 3> uintValues = { 5, 6, 7 };
  273. srgData.SetConstant(uintValueIndex, uintValues);
  274. AZStd::array<float, 4> float4Values = { 10.1f, 11.2f, 12.3f, 14.4f };
  275. srgData.SetConstant(float4ValueIndex, float4Values);
  276. NestedData nestedData[16];
  277. for (uint32_t i = 0; i < 16; ++i)
  278. {
  279. nestedData[i].m_x = (float)i * 2;
  280. nestedData[i].m_y = (float)i * 3;
  281. nestedData[i].m_z = (float)i * 4;
  282. }
  283. // Write the first one as a single element.
  284. srgData.SetConstantRaw(nestedDataIndex, &nestedData[0], sizeof(NestedData));
  285. // Write the second one as an element with an offset.
  286. srgData.SetConstantRaw(nestedDataIndex, &nestedData[1], sizeof(NestedData), sizeof(NestedData));
  287. // Write the next 13 as an array.
  288. srgData.SetConstantRaw(nestedDataIndex, nestedData + 2, sizeof(NestedData) * 2, sizeof(NestedData) * 13);
  289. // Write the last one as a single value with an offset.
  290. srgData.SetConstantRaw(nestedDataIndex, &nestedData[15], sizeof(NestedData) * 15, sizeof(NestedData));
  291. float floatValueResult = srgData.GetConstant<float>(floatValueIndex);
  292. EXPECT_EQ(floatValueResult, floatValue);
  293. const auto ValidateFloat4Values = [&]()
  294. {
  295. AZStd::span<const float> float4ValueResult =
  296. srgData.GetConstantArray<float>(float4ValueIndex);
  297. EXPECT_EQ(float4ValueResult.size(), 4);
  298. EXPECT_EQ(float4ValueResult[0], float4Values[0]);
  299. EXPECT_EQ(float4ValueResult[1], float4Values[1]);
  300. EXPECT_EQ(float4ValueResult[2], float4Values[2]);
  301. EXPECT_EQ(float4ValueResult[3], float4Values[3]);
  302. };
  303. AZStd::span<const uint32_t> uintValuesResult =
  304. srgData.GetConstantArray<uint32_t>(uintValueIndex);
  305. EXPECT_EQ(uintValuesResult.size(), 3);
  306. EXPECT_EQ(uintValuesResult[0], uintValues[0]);
  307. EXPECT_EQ(uintValuesResult[1], uintValues[1]);
  308. EXPECT_EQ(uintValuesResult[2], uintValues[2]);
  309. AZStd::span<const NestedData> nestedDataResult =
  310. srgData.GetConstantArray<NestedData>(nestedDataIndex);
  311. EXPECT_EQ(nestedDataResult.size(), 16);
  312. ValidateFloat4Values();
  313. for (uint32_t i = 0; i < 16; ++i)
  314. {
  315. EXPECT_EQ(nestedDataResult[i].m_x, nestedData[i].m_x);
  316. EXPECT_EQ(nestedDataResult[i].m_y, nestedData[i].m_y);
  317. EXPECT_EQ(nestedDataResult[i].m_z, nestedData[i].m_z);
  318. }
  319. // SetConstant Matrix tests
  320. float matrixValue[16] = {
  321. 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f
  322. };
  323. // SetConstant matrix of type Matrix3x3
  324. const AZ::Matrix3x3& mat3x3Values = AZ::Matrix3x3::CreateFromRowMajorFloat9(matrixValue);
  325. srgData.SetConstant(matrix3x3Index, mat3x3Values);
  326. EXPECT_EQ(srgData.GetConstant<AZ::Matrix3x3>(matrix3x3Index), mat3x3Values);
  327. // SetConstant matrix of type Matrix3x4
  328. const AZ::Matrix3x4& mat3x4Values = AZ::Matrix3x4::CreateFromRowMajorFloat12(matrixValue);
  329. srgData.SetConstant(matrix3x4Index, mat3x4Values);
  330. EXPECT_EQ(srgData.GetConstant<AZ::Matrix3x4>(matrix3x4Index), mat3x4Values);
  331. // SetConstant matrix of type Matrix4x4
  332. const AZ::Matrix4x4& mat4x4Values = AZ::Matrix4x4::CreateFromRowMajorFloat16(matrixValue);
  333. srgData.SetConstant(matrix4x4Index, mat4x4Values);
  334. EXPECT_EQ(srgData.GetConstant<AZ::Matrix4x4>(matrix4x4Index), mat4x4Values);
  335. // Reset the constant matrix3x4Index with identity
  336. srgData.SetConstant(matrix3x4Index, AZ::Matrix3x4::CreateIdentity());
  337. // SetConstant matrix rows, sets 3 rows from 4x4 matrix (which becomes 3x4 matrix)
  338. srgData.SetConstantMatrixRows(matrix3x4Index, mat4x4Values, 3);
  339. EXPECT_EQ(srgData.GetConstant<AZ::Matrix3x4>(matrix3x4Index), mat3x4Values);
  340. // Reset the constant matrix3x3Index with identity
  341. srgData.SetConstant(matrix3x3Index, AZ::Matrix3x3::CreateIdentity());
  342. srgData.SetConstantMatrixRows(matrix3x3Index, mat3x3Values, 3);
  343. EXPECT_EQ(srgData.GetConstant<AZ::Matrix3x3>(matrix3x3Index), mat3x3Values);
  344. // Reset the constant matrix4x4Index with identity
  345. srgData.SetConstant(matrix4x4Index, AZ::Matrix4x4::CreateIdentity());
  346. srgData.SetConstantMatrixRows(matrix4x4Index, mat4x4Values, 4);
  347. EXPECT_EQ(srgData.GetConstant<AZ::Matrix4x4>(matrix4x4Index), mat4x4Values);
  348. // SetConstant
  349. {
  350. // Attempt to a larger amount of data than is supported.
  351. AZ_TEST_START_ASSERTTEST;
  352. srgData.SetConstant(floatValueIndex, AZ::Vector4::CreateOne());
  353. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  354. EXPECT_EQ(srgData.GetConstant<float>(floatValueIndex), floatValue);
  355. // Attempt to assign a smaller amount of data than is supported.
  356. AZ_TEST_START_ASSERTTEST;
  357. srgData.SetConstant(floatValueIndex, uint8_t(0));
  358. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  359. EXPECT_EQ(srgData.GetConstant<float>(floatValueIndex), floatValue);
  360. }
  361. // SetConstant (ArrayIndex)
  362. {
  363. // Assign index that overflows array.
  364. AZ_TEST_START_ASSERTTEST;
  365. srgData.SetConstant(float4ValueIndex, 5.0f, 5);
  366. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  367. ValidateFloat4Values();
  368. // Assign index where alignment doesn't match up.
  369. struct Test
  370. {
  371. uint16_t m_a = 0;
  372. uint16_t m_b = 1;
  373. uint16_t m_c = 2;
  374. };
  375. AZ_TEST_START_ASSERTTEST;
  376. srgData.SetConstant(float4ValueIndex, Test(), 1);
  377. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  378. ValidateFloat4Values();
  379. // Finally, assign a valid value and make sure it get assigned.
  380. float4Values[1] = 99.0f;
  381. srgData.SetConstant(float4ValueIndex, float4Values[1], 1);
  382. ValidateFloat4Values();
  383. }
  384. // SetConstantArray
  385. {
  386. // Attempt to a larger amount of data than is supported.
  387. float float6Values[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f };
  388. AZ_TEST_START_ASSERTTEST;
  389. srgData.SetConstantArray<float>(float4ValueIndex, { float6Values, AZ_ARRAY_SIZE(float6Values) });
  390. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  391. ValidateFloat4Values();
  392. // Attempt to assign a smaller amount of data than is supported.
  393. float float1Value[] = { 5.0f };
  394. AZ_TEST_START_ASSERTTEST;
  395. srgData.SetConstantArray<float>(float4ValueIndex, { float1Value, AZ_ARRAY_SIZE(float1Value) });
  396. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  397. ValidateFloat4Values();
  398. }
  399. }
  400. };
  401. namespace MultiDevice
  402. {
  403. RHI::ShaderResourceGroupData PrepareSRGData(const RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& srgLayout)
  404. {
  405. RHI::Ptr<RHI::ShaderResourceGroupPool> srgPool = aznew AZ::RHI::ShaderResourceGroupPool;
  406. RHI::ShaderResourceGroupPoolDescriptor descriptor;
  407. descriptor.m_layout = srgLayout.get();
  408. srgPool->Init(descriptor);
  409. RHI::Ptr<RHI::ShaderResourceGroup> srg = aznew AZ::RHI::ShaderResourceGroup;
  410. srgPool->InitGroup(*srg);
  411. RHI::ShaderResourceGroupData srgData(*srg);
  412. return srgData;
  413. }
  414. void TestSetConstantVectorsValidCase(const RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& srgLayout)
  415. {
  416. const RHI::ShaderInputConstantIndex vector2index = srgLayout->FindShaderInputConstantIndex(Name("m_vector2"));
  417. EXPECT_EQ(vector2index.GetIndex(), 7);
  418. const RHI::ShaderInputConstantIndex vector3index = srgLayout->FindShaderInputConstantIndex(Name("m_vector3"));
  419. EXPECT_EQ(vector3index.GetIndex(), 8);
  420. const RHI::ShaderInputConstantIndex vector4index = srgLayout->FindShaderInputConstantIndex(Name("m_vector4"));
  421. EXPECT_EQ(vector4index.GetIndex(), 9);
  422. RHI::ShaderResourceGroupData srgData = PrepareSRGData(srgLayout);
  423. const float vector2values[2] = { 1.0f, 2.0f };
  424. const Vector2 vector2 = Vector2::CreateFromFloat2(vector2values);
  425. const float vector3values[3] = { 3.0f, 4.0f, 5.0f };
  426. const Vector3 vector3 = Vector3::CreateFromFloat3(vector3values);
  427. const float vector4values[4] = { 6.0f, 7.0f, 8.0f, 9.0f };
  428. const Vector4 vector4 = Vector4::CreateFromFloat4(vector4values);
  429. EXPECT_TRUE(srgData.SetConstant(vector2index, vector2));
  430. AZStd::span<const uint8_t> resultVector2 = srgData.GetConstantRaw(vector2index);
  431. const Vector2 vector2result = *reinterpret_cast<const Vector2*>(resultVector2.data());
  432. EXPECT_EQ(vector2result, vector2);
  433. EXPECT_TRUE(srgData.SetConstant(vector3index, vector3));
  434. AZStd::span<const uint8_t> resutVector3 = srgData.GetConstantRaw(vector3index);
  435. const Vector3 vector3result = *reinterpret_cast<const Vector3*>(resutVector3.data());
  436. EXPECT_EQ(vector3result, vector3);
  437. EXPECT_TRUE(srgData.SetConstant(vector4index, vector4));
  438. AZStd::span<const uint8_t> resutVector4 = srgData.GetConstantRaw(vector4index);
  439. const Vector4 vector4result = *reinterpret_cast<const Vector4*>(resutVector4.data());
  440. EXPECT_EQ(vector4result, vector4);
  441. }
  442. void TestSetConstantVectorsInvalidCase(const RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& srgLayout)
  443. {
  444. const RHI::ShaderInputConstantIndex vector2index = srgLayout->FindShaderInputConstantIndex(Name("m_vector2"));
  445. EXPECT_EQ(vector2index.GetIndex(), 7);
  446. const RHI::ShaderInputConstantIndex vector3index = srgLayout->FindShaderInputConstantIndex(Name("m_vector3"));
  447. EXPECT_EQ(vector3index.GetIndex(), 8);
  448. const RHI::ShaderInputConstantIndex vector4index = srgLayout->FindShaderInputConstantIndex(Name("m_vector4"));
  449. EXPECT_EQ(vector4index.GetIndex(), 9);
  450. RHI::ShaderResourceGroupData srgData = PrepareSRGData(srgLayout);
  451. const float vector2values[2] = { 1.0f, 2.0f };
  452. const Vector2 vector2 = Vector2::CreateFromFloat2(vector2values);
  453. const float vector3values[3] = { 1.0f, 2.0f, 3.0f };
  454. const Vector3 vector3 = Vector3::CreateFromFloat3(vector3values);
  455. const float vector4values[4] = { 1.0f, 2.0f, 3.0f, 4.0f };
  456. const Vector4 vector4 = Vector4::CreateFromFloat4(vector4values);
  457. // Reset constant vector2index to zero
  458. EXPECT_TRUE(srgData.SetConstant(vector2index, vector2.CreateZero()));
  459. AZ_TEST_START_ASSERTTEST;
  460. EXPECT_FALSE(srgData.SetConstant(vector2index, vector3));
  461. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  462. AZStd::span<const uint8_t> resutV3 = srgData.GetConstantRaw(vector2index);
  463. const Vector3 v3result = *reinterpret_cast<const Vector3*>(resutV3.data());
  464. EXPECT_NE(v3result, vector3);
  465. // Reset constant vector3index to zero
  466. EXPECT_TRUE(srgData.SetConstant(vector3index, vector3.CreateZero()));
  467. AZ_TEST_START_ASSERTTEST;
  468. EXPECT_FALSE(srgData.SetConstant(vector3index, vector4));
  469. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  470. AZStd::span<const uint8_t> resutV4 = srgData.GetConstantRaw(vector3index);
  471. const Vector4 v4result = *reinterpret_cast<const Vector4*>(resutV4.data());
  472. EXPECT_NE(v4result, vector4);
  473. // Reset constant vector4index to zero
  474. EXPECT_TRUE(srgData.SetConstant(vector4index, vector4.CreateZero()));
  475. AZ_TEST_START_ASSERTTEST;
  476. EXPECT_FALSE(srgData.SetConstant(vector4index, vector3));
  477. AZ_TEST_STOP_ASSERTTEST(DeviceCount + 1);
  478. AZStd::span<const uint8_t> resutV3FromIndex4 = srgData.GetConstantRaw(vector4index);
  479. const Vector4 v4resultFromIndex4 = *reinterpret_cast<const Vector4*>(resutV3FromIndex4.data());
  480. EXPECT_NE(v4resultFromIndex4, vector4);
  481. }
  482. void TestGetConstantVectorsValidCase(const RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& srgLayout)
  483. {
  484. const RHI::ShaderInputConstantIndex vector2index = srgLayout->FindShaderInputConstantIndex(Name("m_vector2"));
  485. EXPECT_EQ(vector2index.GetIndex(), 7);
  486. const RHI::ShaderInputConstantIndex vector3index = srgLayout->FindShaderInputConstantIndex(Name("m_vector3"));
  487. EXPECT_EQ(vector3index.GetIndex(), 8);
  488. const RHI::ShaderInputConstantIndex vector4index = srgLayout->FindShaderInputConstantIndex(Name("m_vector4"));
  489. EXPECT_EQ(vector4index.GetIndex(), 9);
  490. RHI::ShaderResourceGroupData srgData = PrepareSRGData(srgLayout);
  491. const float vector2values[2] = { 1.0f, 2.0f };
  492. const Vector2 vector2 = Vector2::CreateFromFloat2(vector2values);
  493. const float vector3values[3] = { 1.0f, 2.0f, 3.0f };
  494. const Vector3 vector3 = Vector3::CreateFromFloat3(vector3values);
  495. const float vector4values[4] = { 1.0f, 2.0f, 3.0f, 4.0f };
  496. const Vector4 vector4 = Vector4::CreateFromFloat4(vector4values);
  497. EXPECT_TRUE(srgData.SetConstantRaw(vector2index, &vector2, 8));
  498. const Vector2 vector2result = srgData.GetConstant<Vector2>(vector2index);
  499. EXPECT_EQ(vector2result, vector2);
  500. EXPECT_TRUE(srgData.SetConstantRaw(vector3index, &vector3, 12));
  501. const Vector3 vector3result = srgData.GetConstant<Vector3>(vector3index);
  502. EXPECT_EQ(vector3result, vector3);
  503. EXPECT_TRUE(srgData.SetConstantRaw(vector4index, &vector4, 16));
  504. const Vector4 vector4result = srgData.GetConstant<Vector4>(vector4index);
  505. EXPECT_EQ(vector4result, vector4);
  506. }
  507. void TestGetConstantVectorsInvalidCase(const RHI::ConstPtr<RHI::ShaderResourceGroupLayout>& srgLayout)
  508. {
  509. const RHI::ShaderInputConstantIndex vector2index = srgLayout->FindShaderInputConstantIndex(Name("m_vector2"));
  510. EXPECT_EQ(vector2index.GetIndex(), 7);
  511. const RHI::ShaderInputConstantIndex vector3index = srgLayout->FindShaderInputConstantIndex(Name("m_vector3"));
  512. EXPECT_EQ(vector3index.GetIndex(), 8);
  513. const RHI::ShaderInputConstantIndex vector4index = srgLayout->FindShaderInputConstantIndex(Name("m_vector4"));
  514. EXPECT_EQ(vector4index.GetIndex(), 9);
  515. RHI::ShaderResourceGroupData srgData = PrepareSRGData(srgLayout);
  516. const float vector2values[2] = { 1.0f, 2.0f };
  517. const Vector2 vector2 = Vector2::CreateFromFloat2(vector2values);
  518. const float vector3values[3] = { 1.0f, 2.0f, 3.0f };
  519. const Vector3 vector3 = Vector3::CreateFromFloat3(vector3values);
  520. const float vector4values[4] = { 1.0f, 2.0f, 3.0f, 4.0f };
  521. const Vector4 vector4 = Vector4::CreateFromFloat4(vector4values);
  522. // Invalid cases for GetConstant
  523. EXPECT_TRUE(srgData.SetConstantRaw(vector2index, &vector2, 8));
  524. AZ_TEST_START_ASSERTTEST;
  525. const Vector3 invalidVector3result = srgData.GetConstant<Vector3>(vector2index);
  526. EXPECT_NE(invalidVector3result, vector3);
  527. AZ_TEST_STOP_ASSERTTEST(1);
  528. EXPECT_TRUE(srgData.SetConstantRaw(vector3index, &vector3, 12));
  529. AZ_TEST_START_ASSERTTEST;
  530. const Vector4 invalidVector4result = srgData.GetConstant<Vector4>(vector3index);
  531. EXPECT_NE(invalidVector4result, vector4);
  532. AZ_TEST_STOP_ASSERTTEST(1);
  533. EXPECT_TRUE(srgData.SetConstantRaw(vector4index, &vector4, 16));
  534. AZ_TEST_START_ASSERTTEST;
  535. const Vector2 invalidVector2result = srgData.GetConstant<Vector2>(vector4index);
  536. EXPECT_NE(invalidVector2result, vector2);
  537. AZ_TEST_STOP_ASSERTTEST(1);
  538. }
  539. TEST_F(MultiDeviceShaderResourceGroupTests, TestShaderResourceGroupLayout)
  540. {
  541. TestShaderResourceGroupLayout();
  542. }
  543. TEST_F(MultiDeviceShaderResourceGroupTests, TestShaderResourceGroupLayoutSerialized)
  544. {
  545. TestShaderResourceGroupLayoutSerialized();
  546. }
  547. TEST_F(MultiDeviceShaderResourceGroupTests, TestShaderResourceGroupPools)
  548. {
  549. TestShaderResourceGroupPools();
  550. }
  551. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataSetConstant_Vectors_ValidOutput)
  552. {
  553. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  554. TestSetConstantVectorsValidCase(srgLayout);
  555. }
  556. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataSetConstant_Vectors_InvalidOutput)
  557. {
  558. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  559. TestSetConstantVectorsInvalidCase(srgLayout);
  560. }
  561. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataGetConstant_Vectors_ValidOutput)
  562. {
  563. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  564. TestGetConstantVectorsValidCase(srgLayout);
  565. }
  566. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataGetConstant_Vectors_InvalidOutput)
  567. {
  568. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateLayout();
  569. TestGetConstantVectorsInvalidCase(srgLayout);
  570. }
  571. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataSetConstant_Vectors_ValidOutput_Serialized)
  572. {
  573. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout;
  574. CreateSerializedLayout(srgLayout);
  575. TestSetConstantVectorsValidCase(srgLayout);
  576. }
  577. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataSetConstant_Vectors_InvalidOutput_Serialized)
  578. {
  579. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout;
  580. CreateSerializedLayout(srgLayout);
  581. TestSetConstantVectorsInvalidCase(srgLayout);
  582. }
  583. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataGetConstant_Vectors_ValidOutput_Serialized)
  584. {
  585. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout;
  586. CreateSerializedLayout(srgLayout);
  587. TestGetConstantVectorsValidCase(srgLayout);
  588. }
  589. TEST_F(MultiDeviceShaderResourceGroupTests, SRGDataGetConstant_Vectors_InvalidOutput_Serialized)
  590. {
  591. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout;
  592. CreateSerializedLayout(srgLayout);
  593. TestGetConstantVectorsInvalidCase(srgLayout);
  594. }
  595. TEST_F(MultiDeviceShaderResourceGroupTests, TestShaderResourceGroupLayoutHash)
  596. {
  597. const Name imageName("m_image");
  598. const Name bufferName("m_buffer");
  599. const Name samplerName("m_sampler");
  600. const Name constantBufferName("m_constantBuffer");
  601. RHI::Ptr<RHI::ShaderResourceGroupLayout> layout = RHI::ShaderResourceGroupLayout::Create();
  602. layout->SetBindingSlot(0);
  603. layout->AddShaderInput(
  604. RHI::ShaderInputImageDescriptor{ imageName, RHI::ShaderInputImageAccess::Read, RHI::ShaderInputImageType::Image2D, 1, 1,
  605. 1
  606. });
  607. layout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  608. bufferName, RHI::ShaderInputBufferAccess::Constant, RHI::ShaderInputBufferType::Constant, 2, UINT_MAX, 3, 3 });
  609. layout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  610. samplerName, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Structured, 3, UINT_MAX, 4, 4 });
  611. layout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{
  612. constantBufferName, RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 6, 6 });
  613. EXPECT_TRUE(layout->Finalize());
  614. {
  615. // Test change name of one shader input
  616. RHI::Ptr<RHI::ShaderResourceGroupLayout> otherLayout = RHI::ShaderResourceGroupLayout::Create();
  617. otherLayout->SetBindingSlot(0);
  618. otherLayout->AddShaderInput(RHI::ShaderInputImageDescriptor{
  619. imageName, RHI::ShaderInputImageAccess::Read, RHI::ShaderInputImageType::Image2D, 1, 1, 1 });
  620. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  621. bufferName, RHI::ShaderInputBufferAccess::Constant, RHI::ShaderInputBufferType::Constant, 2, UINT_MAX, 3, 3 });
  622. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  623. samplerName, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Structured, 3, UINT_MAX, 4, 4 });
  624. otherLayout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{
  625. Name{ "m_constantBuffer2" }, RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 6, 6 });
  626. EXPECT_TRUE(otherLayout->Finalize());
  627. EXPECT_NE(otherLayout->GetHash(), layout->GetHash());
  628. }
  629. {
  630. // Test change of binding slot
  631. RHI::Ptr<RHI::ShaderResourceGroupLayout> otherLayout = RHI::ShaderResourceGroupLayout::Create();
  632. otherLayout->SetBindingSlot(1);
  633. otherLayout->AddShaderInput(RHI::ShaderInputImageDescriptor{
  634. imageName, RHI::ShaderInputImageAccess::Read, RHI::ShaderInputImageType::Image2D, 1, 1, 1 });
  635. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  636. bufferName, RHI::ShaderInputBufferAccess::Constant, RHI::ShaderInputBufferType::Constant, 2, UINT_MAX, 3, 3 });
  637. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  638. samplerName, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Structured, 3, UINT_MAX, 4, 4 });
  639. otherLayout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{
  640. constantBufferName, RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 6, 6 });
  641. EXPECT_TRUE(otherLayout->Finalize());
  642. EXPECT_NE(otherLayout->GetHash(), layout->GetHash());
  643. }
  644. {
  645. // Test adding constants
  646. RHI::Ptr<RHI::ShaderResourceGroupLayout> otherLayout = RHI::ShaderResourceGroupLayout::Create();
  647. otherLayout->SetBindingSlot(0);
  648. otherLayout->AddShaderInput(RHI::ShaderInputImageDescriptor{
  649. imageName, RHI::ShaderInputImageAccess::Read, RHI::ShaderInputImageType::Image2D, 1, 1, 1 });
  650. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  651. bufferName, RHI::ShaderInputBufferAccess::Constant, RHI::ShaderInputBufferType::Constant, 2, UINT_MAX, 3, 3 });
  652. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  653. samplerName, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Structured, 3, UINT_MAX, 4, 4 });
  654. otherLayout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{
  655. constantBufferName, RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 6, 6 });
  656. otherLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{ Name("m_floatValue"), 0, 4, 0, 0 });
  657. EXPECT_TRUE(otherLayout->Finalize());
  658. EXPECT_NE(otherLayout->GetHash(), layout->GetHash());
  659. }
  660. {
  661. // Test adding shader variant key fallback
  662. RHI::Ptr<RHI::ShaderResourceGroupLayout> otherLayout = RHI::ShaderResourceGroupLayout::Create();
  663. otherLayout->SetBindingSlot(0);
  664. otherLayout->AddShaderInput(RHI::ShaderInputImageDescriptor{
  665. imageName, RHI::ShaderInputImageAccess::Read, RHI::ShaderInputImageType::Image2D, 1, 1, 1 });
  666. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  667. bufferName, RHI::ShaderInputBufferAccess::Constant, RHI::ShaderInputBufferType::Constant, 2, UINT_MAX, 3, 3 });
  668. otherLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  669. samplerName, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Structured, 3, UINT_MAX, 4, 4 });
  670. otherLayout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{
  671. constantBufferName, RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 6, 6 });
  672. otherLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{ Name("m_floatValue"), 0, 4, 0, 0 });
  673. otherLayout->SetShaderVariantKeyFallback(Name{ "m_floatValue" }, 1);
  674. EXPECT_TRUE(otherLayout->Finalize());
  675. EXPECT_NE(otherLayout->GetHash(), layout->GetHash());
  676. }
  677. }
  678. } // namespace MultiDevice
  679. } // namespace UnitTest