ShaderResourceGroupTests.cpp 38 KB

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