3
0

MaterialTests.cpp 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473
  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 <AzTest/AzTest.h>
  9. #include <Common/RPITestFixture.h>
  10. #include <Common/ErrorMessageFinder.h>
  11. #include <Common/ShaderAssetTestUtils.h>
  12. #include <Material/MaterialAssetTestUtils.h>
  13. #include <Atom/RPI.Public/ColorManagement/TransformColor.h>
  14. #include <Atom/RPI.Public/Material/Material.h>
  15. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  16. #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
  17. #include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
  18. #include <Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h>
  19. #include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
  20. #include <Atom/RPI.Reflect/Image/ImageMipChainAssetCreator.h>
  21. #include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
  22. #include <Atom/RPI.Edit/Material/MaterialFunctorSourceData.h>
  23. namespace UnitTest
  24. {
  25. using namespace AZ;
  26. using namespace RPI;
  27. class MaterialTests
  28. : public RPITestFixture
  29. {
  30. protected:
  31. Data::Asset<ShaderAsset> m_testMaterialShaderAsset;
  32. AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> m_testMaterialSrgLayout;
  33. Data::Asset<MaterialTypeAsset> m_testMaterialTypeAsset;
  34. Data::Asset<MaterialAsset> m_testMaterialAsset;
  35. Data::Asset<StreamingImageAsset> m_testImageAsset;
  36. Data::Asset<AttachmentImageAsset> m_testAttachmentImageAsset;
  37. Data::Instance<StreamingImage> m_testImage;
  38. Data::Instance<AttachmentImage> m_testAttachmentImage;
  39. Data::Asset<StreamingImageAsset> CreateTestImageAsset() const
  40. {
  41. Data::Asset<StreamingImageAsset> testImageAsset;
  42. Data::Asset<ImageMipChainAsset> mipChainAsset;
  43. ImageMipChainAssetCreator mipChainCreator;
  44. mipChainCreator.Begin(Uuid::CreateRandom(), 1, 1);
  45. mipChainCreator.BeginMip(RHI::GetImageSubresourceLayout(RHI::Size{ 1,1,1 }, RHI::Format::R8_UNORM));
  46. uint8_t pixel = 0;
  47. mipChainCreator.AddSubImage(&pixel, 1);
  48. mipChainCreator.EndMip();
  49. mipChainCreator.End(mipChainAsset);
  50. StreamingImageAssetCreator imageCreator;
  51. imageCreator.Begin(Uuid::CreateRandom());
  52. imageCreator.AddMipChainAsset(*mipChainAsset.Get());
  53. imageCreator.SetFlags(StreamingImageFlags::NotStreamable);
  54. imageCreator.SetPoolAssetId(ImageSystemInterface::Get()->GetSystemStreamingPool()->GetAssetId());
  55. imageCreator.End(testImageAsset);
  56. return testImageAsset;
  57. }
  58. Data::Asset<AttachmentImageAsset> CreateAttachmentImageAsset() const
  59. {
  60. Data::Asset<AttachmentImageAsset> testImageAsset;
  61. AttachmentImageAssetCreator imageCreator;
  62. imageCreator.Begin(Uuid::CreateRandom());
  63. imageCreator.SetPoolAsset({ImageSystemInterface::Get()->GetSystemAttachmentPool()->GetAssetId(), azrtti_typeid<ResourcePoolAsset>()});
  64. imageCreator.SetName(Name("testAttachmentImageAsset"), true);
  65. imageCreator.End(testImageAsset);
  66. return testImageAsset;
  67. }
  68. void SetUp() override
  69. {
  70. RPITestFixture::SetUp();
  71. m_testMaterialSrgLayout = CreateCommonTestMaterialSrgLayout();
  72. m_testMaterialShaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout);
  73. MaterialTypeAssetCreator materialTypeCreator;
  74. materialTypeCreator.Begin(Uuid::CreateRandom());
  75. materialTypeCreator.AddShader(m_testMaterialShaderAsset);
  76. AddCommonTestMaterialProperties(materialTypeCreator);
  77. materialTypeCreator.SetPropertyValue(Name{ "MyFloat2" }, Vector2{ 10.1f, 10.2f });
  78. materialTypeCreator.SetPropertyValue(Name{ "MyFloat3" }, Vector3{ 11.1f, 11.2f, 11.3f });
  79. materialTypeCreator.SetPropertyValue(Name{ "MyFloat4" }, Vector4{ 12.1f, 12.2f, 12.3f, 12.4f });
  80. materialTypeCreator.SetPropertyValue(Name{ "MyColor" }, Color{ 0.1f, 0.2f, 0.3f, 0.4f });
  81. materialTypeCreator.SetPropertyValue(Name{ "MyInt" }, -12);
  82. materialTypeCreator.SetPropertyValue(Name{ "MyUInt" }, 112u);
  83. materialTypeCreator.SetPropertyValue(Name{ "MyFloat" }, 11.5f);
  84. materialTypeCreator.SetPropertyValue(Name{ "MyEnum" }, 1u);
  85. materialTypeCreator.End(m_testMaterialTypeAsset);
  86. m_testImageAsset = CreateTestImageAsset();
  87. m_testImage = StreamingImage::FindOrCreate(m_testImageAsset);
  88. m_testAttachmentImageAsset = CreateAttachmentImageAsset();
  89. m_testAttachmentImage = AttachmentImage::FindOrCreate(m_testAttachmentImageAsset);
  90. MaterialAssetCreator materialCreator;
  91. materialCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  92. materialCreator.SetPropertyValue(Name{ "MyFloat2" }, Vector2{ 0.1f, 0.2f });
  93. materialCreator.SetPropertyValue(Name{ "MyFloat3" }, Vector3{ 1.1f, 1.2f, 1.3f });
  94. materialCreator.SetPropertyValue(Name{ "MyFloat4" }, Vector4{ 2.1f, 2.2f, 2.3f, 2.4f });
  95. materialCreator.SetPropertyValue(Name{ "MyColor" }, Color{ 1.0f, 1.0f, 1.0f, 1.0f });
  96. materialCreator.SetPropertyValue(Name{ "MyInt" }, -2);
  97. materialCreator.SetPropertyValue(Name{ "MyUInt" }, 12u);
  98. materialCreator.SetPropertyValue(Name{ "MyFloat" }, 1.5f);
  99. materialCreator.SetPropertyValue(Name{ "MyBool" }, true);
  100. materialCreator.SetPropertyValue(Name{ "MyImage" }, Data::Asset<ImageAsset>(m_testImageAsset));
  101. materialCreator.SetPropertyValue(Name{ "MyEnum" }, 2u);
  102. materialCreator.SetPropertyValue(Name{ "MyAttachmentImage" }, Data::Asset<ImageAsset>(m_testAttachmentImageAsset));
  103. materialCreator.End(m_testMaterialAsset);
  104. }
  105. void TearDown() override
  106. {
  107. m_testMaterialShaderAsset.Reset();
  108. m_testMaterialSrgLayout = nullptr;
  109. m_testMaterialTypeAsset.Reset();
  110. m_testMaterialAsset.Reset();
  111. m_testImageAsset.Reset();
  112. m_testImage = nullptr;
  113. m_testAttachmentImageAsset.Reset();
  114. m_testAttachmentImage = nullptr;
  115. RPITestFixture::TearDown();
  116. }
  117. void ValidateInitialValuesFromMaterialType(Data::Instance<Material> material)
  118. {
  119. // Test reading the values directly...
  120. EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" })), false);
  121. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -12);
  122. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" })), 112u);
  123. EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 11.5f);
  124. EXPECT_EQ(material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" })), Vector2(10.1f, 10.2f));
  125. EXPECT_EQ(material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" })), Vector3(11.1f, 11.2f, 11.3f));
  126. EXPECT_EQ(material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
  127. EXPECT_EQ(material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" })), Color(0.1f, 0.2f, 0.3f, 0.4f));
  128. EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" })), nullptr);
  129. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" })), 1u);
  130. // Dig in to the SRG to make sure the values were applied there as well...
  131. const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
  132. const RHI::ShaderResourceGroupData& srgData = srg->GetData();
  133. EXPECT_EQ(srgData.GetConstant<bool>(srgData.FindShaderInputConstantIndex(Name{ "m_bool" })), false);
  134. EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), -12);
  135. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 112u);
  136. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 11.5f);
  137. EXPECT_EQ(srgData.GetConstant<Vector2>(srgData.FindShaderInputConstantIndex(Name{ "m_float2" })), Vector2(10.1f, 10.2f));
  138. // Currently srgData.GetConstant<Vector3> isn't supported so we check the individual floats
  139. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 0), 11.1f);
  140. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 1), 11.2f);
  141. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 2), 11.3f);
  142. EXPECT_EQ(srgData.GetConstant<Vector4>(srgData.FindShaderInputConstantIndex(Name{ "m_float4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
  143. EXPECT_EQ(srgData.GetConstant<Color>(srgData.FindShaderInputConstantIndex(Name{ "m_color" })), TransformColor(Color(0.1f, 0.2f, 0.3f, 0.4f), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg));
  144. EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), nullptr);
  145. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 1u);
  146. EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_attachmentImage" }), 0), nullptr);
  147. }
  148. void ValidateInitialValuesFromMaterial(Data::Instance<Material> material)
  149. {
  150. // Test reading the values directly...
  151. EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" })), true);
  152. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -2);
  153. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" })), 12u);
  154. EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 1.5f);
  155. EXPECT_EQ(material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" })), Vector2(0.1f, 0.2f));
  156. EXPECT_EQ(material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" })), Vector3(1.1f, 1.2f, 1.3f));
  157. EXPECT_EQ(material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" })), Vector4(2.1f, 2.2f, 2.3f, 2.4f));
  158. EXPECT_EQ(material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" })), Color(1.0f, 1.0f, 1.0f, 1.0f));
  159. EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" })), m_testImage);
  160. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" })), 2u);
  161. EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyAttachmentImage" })), m_testAttachmentImage);
  162. // Dig in to the SRG to make sure the values were applied there as well...
  163. const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
  164. const RHI::ShaderResourceGroupData& srgData = srg->GetData();
  165. EXPECT_EQ(srgData.GetConstant<bool>(srgData.FindShaderInputConstantIndex(Name{ "m_bool" })), true);
  166. EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), -2);
  167. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 12u);
  168. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 1.5f);
  169. EXPECT_EQ(srgData.GetConstant<Vector2>(srgData.FindShaderInputConstantIndex(Name{ "m_float2" })), Vector2(0.1f, 0.2f));
  170. // Currently srgData.GetConstant<Vector3> isn't supported so we check the individual floats
  171. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 0), 1.1f);
  172. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 1), 1.2f);
  173. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 2), 1.3f);
  174. EXPECT_EQ(srgData.GetConstant<Vector4>(srgData.FindShaderInputConstantIndex(Name{ "m_float4" })), Vector4(2.1f, 2.2f, 2.3f, 2.4f));
  175. EXPECT_EQ(srgData.GetConstant<Color>(srgData.FindShaderInputConstantIndex(Name{ "m_color" })), TransformColor(Color(1.0f, 1.0f, 1.0f, 1.0f), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg));
  176. EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), m_testImage->GetImageView());
  177. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 2u);
  178. EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_attachmentImage" }), 0), m_testAttachmentImage->GetImageView());
  179. }
  180. //! Provides write access to private material asset property values, primarily for simulating
  181. //! MaterialAsset hot reload.
  182. MaterialPropertyValue& AccessMaterialAssetPropertyValue(Data::Asset<MaterialAsset> materialAsset, Name propertyName)
  183. {
  184. return materialAsset->m_propertyValues[materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyName).GetIndex()];
  185. }
  186. };
  187. //! Copies the value of a material property to an internal material pipelien property
  188. class SetInternalPropertyFunctor final
  189. : public AZ::RPI::MaterialFunctor
  190. {
  191. friend class SetInternalPropertyFunctorSourceData;
  192. public:
  193. AZ_CLASS_ALLOCATOR(SetInternalPropertyFunctor, SystemAllocator)
  194. using AZ::RPI::MaterialFunctor::Process;
  195. void Process(AZ::RPI::MaterialFunctorAPI::RuntimeContext& context) override
  196. {
  197. const MaterialPropertyValue value = context.GetMaterialPropertyValue(m_inputPropertyIndex);
  198. context.SetInternalMaterialPropertyValue(m_outputPropertyName, value);
  199. }
  200. private:
  201. AZ::RPI::MaterialPropertyIndex m_inputPropertyIndex;
  202. AZ::Name m_outputPropertyName;
  203. };
  204. class SetInternalPropertyFunctorSourceData final
  205. : public AZ::RPI::MaterialFunctorSourceData
  206. {
  207. public:
  208. AZ_CLASS_ALLOCATOR(SetInternalPropertyFunctorSourceData, AZ::SystemAllocator)
  209. using MaterialFunctorSourceData::CreateFunctor;
  210. FunctorResult CreateFunctor(const RuntimeContext& context) const override
  211. {
  212. Ptr<SetInternalPropertyFunctor> functor = aznew SetInternalPropertyFunctor;
  213. functor->m_inputPropertyIndex = context.FindMaterialPropertyIndex(Name{m_inputPropertyName});
  214. functor->m_outputPropertyName = m_outputPropertyName;
  215. AddMaterialPropertyDependency(functor, functor->m_inputPropertyIndex);
  216. return Success(Ptr<MaterialFunctor>(functor));
  217. }
  218. AZ::Name m_inputPropertyName;
  219. AZ::Name m_outputPropertyName;
  220. };
  221. //! Sets the shader enable state based on a bool material property
  222. class ShaderEnablePipelineFunctor final
  223. : public AZ::RPI::MaterialFunctor
  224. {
  225. public:
  226. AZ_CLASS_ALLOCATOR(ShaderEnablePipelineFunctor, SystemAllocator)
  227. using AZ::RPI::MaterialFunctor::Process;
  228. void Process(AZ::RPI::MaterialFunctorAPI::PipelineRuntimeContext& context) override
  229. {
  230. const bool enable = context.GetMaterialPropertyValue(m_enablePropertyIndex).GetValue<bool>();
  231. context.SetShaderEnabled(m_shaderTag, enable);
  232. }
  233. AZ::RPI::MaterialPropertyIndex m_enablePropertyIndex;
  234. AZ::Name m_shaderTag;
  235. };
  236. class ShaderEnablePipelineFunctorSourceData final
  237. : public AZ::RPI::MaterialFunctorSourceData
  238. {
  239. public:
  240. AZ_CLASS_ALLOCATOR(ShaderEnablePipelineFunctorSourceData, AZ::SystemAllocator)
  241. using MaterialFunctorSourceData::CreateFunctor;
  242. FunctorResult CreateFunctor(const RuntimeContext& context) const override
  243. {
  244. Ptr<ShaderEnablePipelineFunctor> functor = aznew ShaderEnablePipelineFunctor;
  245. functor->m_enablePropertyIndex = context.FindMaterialPropertyIndex(Name{m_enablePropertyName});
  246. functor->m_shaderTag = m_shaderTag;
  247. AddMaterialPropertyDependency(functor, functor->m_enablePropertyIndex);
  248. return Success(Ptr<MaterialFunctor>(functor));
  249. }
  250. AZ::Name m_enablePropertyName;
  251. AZ::Name m_shaderTag;
  252. };
  253. TEST_F(MaterialTests, TestCreateVsFindOrCreate)
  254. {
  255. Data::Instance<Material> materialInstance1 = Material::FindOrCreate(m_testMaterialAsset);
  256. Data::Instance<Material> materialInstance2 = Material::FindOrCreate(m_testMaterialAsset);
  257. Data::Instance<Material> materialInstance3 = Material::Create(m_testMaterialAsset);
  258. Data::Instance<Material> materialInstance4 = Material::Create(m_testMaterialAsset);
  259. EXPECT_TRUE(materialInstance1);
  260. EXPECT_TRUE(materialInstance2);
  261. EXPECT_TRUE(materialInstance3);
  262. EXPECT_TRUE(materialInstance4);
  263. // Instances created via FindOrCreate should be the same object
  264. EXPECT_EQ(materialInstance1, materialInstance2);
  265. EXPECT_NE(materialInstance1, materialInstance3);
  266. EXPECT_NE(materialInstance1, materialInstance4);
  267. EXPECT_NE(materialInstance3, materialInstance4);
  268. }
  269. TEST_F(MaterialTests, TestInitialValuesFromMaterial)
  270. {
  271. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  272. ValidateInitialValuesFromMaterial(material);
  273. }
  274. TEST_F(MaterialTests, TestSetPropertyValue)
  275. {
  276. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  277. Data::Asset<StreamingImageAsset> otherTestImageAsset;
  278. Data::Instance<StreamingImage> otherTestImage;
  279. otherTestImageAsset = CreateTestImageAsset();
  280. otherTestImage = StreamingImage::FindOrCreate(otherTestImageAsset);
  281. EXPECT_TRUE(material->SetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" }), false));
  282. EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" }), -5));
  283. EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" }), 123u));
  284. EXPECT_TRUE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f));
  285. EXPECT_TRUE(material->SetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" }), Vector2(10.1f, 10.2f)));
  286. EXPECT_TRUE(material->SetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" }), Vector3(11.1f, 11.2f, 11.3f)));
  287. EXPECT_TRUE(material->SetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" }), Vector4(12.1f, 12.2f, 12.3f, 12.4f)));
  288. EXPECT_TRUE(material->SetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" }), Color(0.1f, 0.2f, 0.3f, 0.4f)));
  289. EXPECT_TRUE(material->SetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" }), otherTestImage));
  290. EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" }), 3u));
  291. // Test reading the values directly...
  292. EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" })), false);
  293. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -5);
  294. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" })), 123u);
  295. EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 2.5f);
  296. EXPECT_EQ(material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" })), Vector2(10.1f, 10.2f));
  297. EXPECT_EQ(material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" })), Vector3(11.1f, 11.2f, 11.3f));
  298. EXPECT_EQ(material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
  299. EXPECT_EQ(material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" })), Color(0.1f, 0.2f, 0.3f, 0.4f));
  300. EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" })), otherTestImage);
  301. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" })), 3u);
  302. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  303. material->Compile();
  304. // Dig in to the SRG to make sure the values were applied there as well...
  305. const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
  306. const RHI::ShaderResourceGroupData& srgData = srg->GetData();
  307. EXPECT_EQ(srgData.GetConstant<bool>(srgData.FindShaderInputConstantIndex(Name{ "m_bool" })), false);
  308. EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), -5);
  309. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 123u);
  310. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 2.5f);
  311. EXPECT_EQ(srgData.GetConstant<Vector2>(srgData.FindShaderInputConstantIndex(Name{ "m_float2" })), Vector2(10.1f, 10.2f));
  312. // Currently srgData.GetConstant<Vector3> isn't supported so we check the individual floats
  313. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 0), 11.1f);
  314. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 1), 11.2f);
  315. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 2), 11.3f);
  316. EXPECT_EQ(srgData.GetConstant<Vector4>(srgData.FindShaderInputConstantIndex(Name{ "m_float4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
  317. EXPECT_EQ(srgData.GetConstant<Color>(srgData.FindShaderInputConstantIndex(Name{ "m_color" })), TransformColor(Color(0.1f, 0.2f, 0.3f, 0.4f), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg));
  318. EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), otherTestImage->GetImageView());
  319. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 3u);
  320. }
  321. TEST_F(MaterialTests, TestSetPropertyValueToMultipleShaderSettings)
  322. {
  323. Data::Asset<MaterialTypeAsset> materialTypeAsset;
  324. Data::Asset<MaterialAsset> materialAsset;
  325. MaterialTypeAssetCreator materialTypeCreator;
  326. materialTypeCreator.Begin(Uuid::CreateRandom());
  327. materialTypeCreator.AddShader(m_testMaterialShaderAsset);
  328. materialTypeCreator.BeginMaterialProperty(Name{ "MyInt" }, MaterialPropertyDataType::Int);
  329. materialTypeCreator.ConnectMaterialPropertyToShaderInput(Name{ "m_int" });
  330. materialTypeCreator.ConnectMaterialPropertyToShaderInput(Name{ "m_uint" });
  331. materialTypeCreator.EndMaterialProperty();
  332. materialTypeCreator.End(materialTypeAsset);
  333. MaterialAssetCreator materialAssetCreator;
  334. materialAssetCreator.Begin(Uuid::CreateRandom(), materialTypeAsset);
  335. materialAssetCreator.End(materialAsset);
  336. Data::Instance<Material> material = Material::FindOrCreate(materialAsset);
  337. EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" }), 42));
  338. // Test reading the value directly...
  339. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), 42);
  340. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  341. material->Compile();
  342. // Dig in to the SRG to make sure the values were applied to both shader constants...
  343. const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
  344. const RHI::ShaderResourceGroupData& srgData = srg->GetData();
  345. EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), 42);
  346. EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 42u);
  347. }
  348. TEST_F(MaterialTests, TestSetPropertyValueWhenValueIsUnchanged)
  349. {
  350. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  351. EXPECT_TRUE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f));
  352. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  353. EXPECT_TRUE(material->Compile());
  354. // Taint the SRG so we can check whether it was set by the SetPropertyValue() calls below.
  355. const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
  356. const RHI::ShaderResourceGroupData& srgData = srg->GetData();
  357. const_cast<RHI::ShaderResourceGroupData*>(&srgData)->SetConstant(m_testMaterialSrgLayout->FindShaderInputConstantIndex(Name{"m_float"}), 0.0f);
  358. // Set the properties to the same values as before
  359. EXPECT_FALSE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f));
  360. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  361. EXPECT_TRUE(material->Compile());
  362. // Make sure the SRG is still tainted, because the SetPropertyValue() functions weren't processed
  363. EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 0.0f);
  364. }
  365. TEST_F(MaterialTests, TestImageNotProvided)
  366. {
  367. Data::Asset<MaterialAsset> materialAssetWithEmptyImage;
  368. MaterialAssetCreator materialCreator;
  369. materialCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  370. materialCreator.SetPropertyValue(Name{"MyFloat2"}, Vector2{0.1f, 0.2f});
  371. materialCreator.SetPropertyValue(Name{"MyFloat3"}, Vector3{1.1f, 1.2f, 1.3f});
  372. materialCreator.SetPropertyValue(Name{"MyFloat4"}, Vector4{2.1f, 2.2f, 2.3f, 2.4f});
  373. materialCreator.SetPropertyValue(Name{"MyColor"}, Color{1.0f, 1.0f, 1.0f, 1.0f});
  374. materialCreator.SetPropertyValue(Name{"MyInt"}, -2);
  375. materialCreator.SetPropertyValue(Name{"MyUInt"}, 12u);
  376. materialCreator.SetPropertyValue(Name{"MyFloat"}, 1.5f);
  377. materialCreator.SetPropertyValue(Name{"MyBool"}, true);
  378. // We don't set "MyImage"
  379. materialCreator.End(materialAssetWithEmptyImage);
  380. Data::Instance<Material> material = Material::FindOrCreate(materialAssetWithEmptyImage);
  381. Data::Instance<Image> nullImageInstance;
  382. Data::Instance<Image> actualImageInstance = material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{"MyImage"}));
  383. EXPECT_EQ(actualImageInstance, nullImageInstance);
  384. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  385. material->Compile();
  386. const RHI::ShaderResourceGroupData& srgData = material->GetRHIShaderResourceGroup()->GetData();
  387. EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{"m_image"}), 0), nullptr);
  388. }
  389. TEST_F(MaterialTests, TestMaterialWithNoSRGOrProperties)
  390. {
  391. // Making a material with no properties and no SRG allows us to create simple shaders
  392. // that don't need any input, for example a debug shader that just renders surface normals.
  393. Data::Asset<MaterialTypeAsset> emptyMaterialTypeAsset;
  394. MaterialTypeAssetCreator materialTypeCreator;
  395. materialTypeCreator.Begin(Uuid::CreateRandom());
  396. EXPECT_TRUE(materialTypeCreator.End(emptyMaterialTypeAsset));
  397. Data::Asset<MaterialAsset> emptyMaterialAsset;
  398. MaterialAssetCreator materialCreator;
  399. materialCreator.Begin(Uuid::CreateRandom(), emptyMaterialTypeAsset);
  400. EXPECT_TRUE(materialCreator.End(emptyMaterialAsset));
  401. Data::Instance<Material> material = Material::FindOrCreate(emptyMaterialAsset);
  402. EXPECT_TRUE(material);
  403. EXPECT_FALSE(material->GetRHIShaderResourceGroup());
  404. }
  405. Ptr<ShaderOptionGroupLayout> CreateTestOptionsLayout()
  406. {
  407. AZStd::vector<RPI::ShaderOptionValuePair> enumOptionValues = CreateEnumShaderOptionValues({"Low", "Med", "High"});
  408. AZStd::vector<RPI::ShaderOptionValuePair> boolOptionValues = CreateBoolShaderOptionValues();
  409. AZStd::vector<RPI::ShaderOptionValuePair> rangeOptionValues = CreateIntRangeShaderOptionValues(1, 10);
  410. Ptr<ShaderOptionGroupLayout> shaderOptions = ShaderOptionGroupLayout::Create();
  411. uint32_t order = 0;
  412. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enumA"}, ShaderOptionType::Enumeration, 0, order++, enumOptionValues, Name{"Low"}});
  413. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enumB"}, ShaderOptionType::Enumeration, 2, order++, enumOptionValues, Name{"Low"}});
  414. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enumC"}, ShaderOptionType::Enumeration, 4, order++, enumOptionValues, Name{"Low"}});
  415. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_boolA"}, ShaderOptionType::Boolean, 6, order++, boolOptionValues, Name{"False"}});
  416. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_boolB"}, ShaderOptionType::Boolean, 7, order++, boolOptionValues, Name{"False"}});
  417. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_boolC"}, ShaderOptionType::Boolean, 8, order++, boolOptionValues, Name{"False"}});
  418. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_rangeA"}, ShaderOptionType::IntegerRange, 9, order++, rangeOptionValues, Name{"1"}});
  419. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_rangeB"}, ShaderOptionType::IntegerRange, 13, order++, rangeOptionValues, Name{"1"}});
  420. shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_rangeC"}, ShaderOptionType::IntegerRange, 17, order++, rangeOptionValues, Name{"1"}});
  421. shaderOptions->Finalize();
  422. return shaderOptions;
  423. }
  424. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToShaderOptions_AllTypes)
  425. {
  426. Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
  427. Data::Asset<ShaderAsset> shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
  428. MaterialTypeAssetCreator materialTypeCreator;
  429. materialTypeCreator.Begin(Uuid::CreateRandom());
  430. materialTypeCreator.AddShader(shaderAsset);
  431. materialTypeCreator.BeginMaterialProperty(Name{"EnumA"}, MaterialPropertyDataType::Int);
  432. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_enumA"});
  433. materialTypeCreator.EndMaterialProperty();
  434. materialTypeCreator.BeginMaterialProperty(Name{"EnumB"}, MaterialPropertyDataType::UInt);
  435. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_enumB"});
  436. materialTypeCreator.EndMaterialProperty();
  437. materialTypeCreator.BeginMaterialProperty(Name{"Bool"}, MaterialPropertyDataType::Bool);
  438. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_boolA"});
  439. materialTypeCreator.EndMaterialProperty();
  440. materialTypeCreator.BeginMaterialProperty(Name{"RangeA"}, MaterialPropertyDataType::Int);
  441. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_rangeA"});
  442. materialTypeCreator.EndMaterialProperty();
  443. materialTypeCreator.BeginMaterialProperty(Name{"RangeB"}, MaterialPropertyDataType::UInt);
  444. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_rangeB"});
  445. materialTypeCreator.EndMaterialProperty();
  446. materialTypeCreator.SetPropertyValue(Name{"EnumA"}, 2);
  447. materialTypeCreator.SetPropertyValue(Name{"EnumB"}, 1u);
  448. materialTypeCreator.SetPropertyValue(Name{"Bool"}, true);
  449. materialTypeCreator.SetPropertyValue(Name{"RangeA"}, 5);
  450. materialTypeCreator.SetPropertyValue(Name{"RangeB"}, 10u);
  451. materialTypeCreator.End(m_testMaterialTypeAsset);
  452. MaterialAssetCreator materialAssetCreator;
  453. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  454. materialAssetCreator.End(m_testMaterialAsset);
  455. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  456. auto& optionEnumA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_enumA"}));
  457. auto& optionEnumB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_enumB"}));
  458. auto& optionBoolA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_boolA"}));
  459. auto& optionRangeA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeA"}));
  460. auto& optionRangeB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeB"}));
  461. // Check the values on the properties themselves
  462. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"EnumA"})), 2);
  463. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"EnumB"})), 1u);
  464. EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{"Bool"})), true);
  465. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"RangeA"})), 5);
  466. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"})), 10u);
  467. // Check the values on the underlying ShaderCollection::Item
  468. ShaderOptionGroup options{optionsLayout, material->GetGeneralShaderCollection()[0].GetShaderVariantId()};
  469. EXPECT_EQ(optionEnumA.Get(options).GetIndex(), optionEnumA.FindValue(Name{"High"}).GetIndex());
  470. EXPECT_EQ(optionEnumB.Get(options).GetIndex(), optionEnumB.FindValue(Name{"Med"}).GetIndex());
  471. EXPECT_EQ(optionBoolA.Get(options).GetIndex(), optionBoolA.FindValue(Name{"True"}).GetIndex());
  472. EXPECT_EQ(optionRangeA.Get(options).GetIndex(), 5);
  473. EXPECT_EQ(optionRangeB.Get(options).GetIndex(), 10);
  474. // Now call SetPropertyValue to change the values, and check again
  475. EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"EnumA"}), 1));
  476. EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"EnumB"}), 0u));
  477. EXPECT_TRUE(material->SetPropertyValue<bool>(material->FindPropertyIndex(Name{"Bool"}), false));
  478. EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"RangeA"}), 3));
  479. EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"}), 7u));
  480. // Check the values on the properties themselves
  481. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"EnumA"})), 1);
  482. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"EnumB"})), 0u);
  483. EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{"Bool"})), false);
  484. EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"RangeA"})), 3);
  485. EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"})), 7u);
  486. ProcessQueuedSrgCompilations(shaderAsset, m_testMaterialSrgLayout->GetName());
  487. material->Compile();
  488. // Check the values on the underlying ShaderCollection::Item
  489. ShaderOptionGroup options2{optionsLayout, material->GetGeneralShaderCollection()[0].GetShaderVariantId()};
  490. EXPECT_EQ(optionEnumA.Get(options2).GetIndex(), optionEnumA.FindValue(Name{"Med"}).GetIndex());
  491. EXPECT_EQ(optionEnumB.Get(options2).GetIndex(), optionEnumB.FindValue(Name{"Low"}).GetIndex());
  492. EXPECT_EQ(optionBoolA.Get(options2).GetIndex(), optionBoolA.FindValue(Name{"False"}).GetIndex());
  493. EXPECT_EQ(optionRangeA.Get(options2).GetIndex(), 3);
  494. EXPECT_EQ(optionRangeB.Get(options2).GetIndex(), 7);
  495. }
  496. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToShaderOptions_WithMultipleShaders)
  497. {
  498. Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
  499. Data::Asset<ShaderAsset> shaderAsset =
  500. CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
  501. MaterialTypeAssetCreator materialTypeCreator;
  502. materialTypeCreator.Begin(Uuid::CreateRandom());
  503. // Adding more than one shader
  504. materialTypeCreator.AddShader(shaderAsset);
  505. materialTypeCreator.AddShader(shaderAsset);
  506. materialTypeCreator.AddShader(shaderAsset);
  507. materialTypeCreator.BeginMaterialProperty(Name{"Value"}, MaterialPropertyDataType::Int);
  508. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_rangeB"}); // Applies to all shaders
  509. materialTypeCreator.EndMaterialProperty();
  510. materialTypeCreator.SetPropertyValue(Name{"Value"}, 2);
  511. materialTypeCreator.End(m_testMaterialTypeAsset);
  512. MaterialAssetCreator materialAssetCreator;
  513. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  514. materialAssetCreator.End(m_testMaterialAsset);
  515. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  516. auto& optionRangeB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeB"}));
  517. const ShaderCollection& shaderCollection = material->GetGeneralShaderCollection();
  518. // Check the values on the underlying ShaderVariantReferences
  519. {
  520. ShaderOptionGroup options0{optionsLayout, shaderCollection[0].GetShaderVariantId()};
  521. ShaderOptionGroup options1{optionsLayout, shaderCollection[1].GetShaderVariantId()};
  522. ShaderOptionGroup options2{optionsLayout, shaderCollection[2].GetShaderVariantId()};
  523. EXPECT_EQ(optionRangeB.Get(options0).GetIndex(), 2);
  524. EXPECT_EQ(optionRangeB.Get(options1).GetIndex(), 2);
  525. EXPECT_EQ(optionRangeB.Get(options2).GetIndex(), 2);
  526. }
  527. // Now call SetPropertyValue to change the values, and check again
  528. EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"Value"}), 5));
  529. ProcessQueuedSrgCompilations(shaderAsset, m_testMaterialSrgLayout->GetName());
  530. material->Compile();
  531. // Check the values on the underlying ShaderVariantReferences
  532. {
  533. ShaderOptionGroup options0{optionsLayout, shaderCollection[0].GetShaderVariantId()};
  534. ShaderOptionGroup options1{optionsLayout, shaderCollection[1].GetShaderVariantId()};
  535. ShaderOptionGroup options2{optionsLayout, shaderCollection[2].GetShaderVariantId()};
  536. EXPECT_EQ(optionRangeB.Get(options0).GetIndex(), 5);
  537. EXPECT_EQ(optionRangeB.Get(options1).GetIndex(), 5);
  538. EXPECT_EQ(optionRangeB.Get(options2).GetIndex(), 5);
  539. }
  540. }
  541. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToShaderEnabled)
  542. {
  543. MaterialTypeAssetCreator materialTypeCreator;
  544. materialTypeCreator.Begin(Uuid::CreateRandom());
  545. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"one"});
  546. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"two"});
  547. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"three"});
  548. materialTypeCreator.BeginMaterialProperty(Name{"EnableSecondShader"}, MaterialPropertyDataType::Bool);
  549. materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"two"});
  550. materialTypeCreator.EndMaterialProperty();
  551. materialTypeCreator.End(m_testMaterialTypeAsset);
  552. MaterialAssetCreator materialAssetCreator;
  553. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  554. materialAssetCreator.End(m_testMaterialAsset);
  555. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  556. MaterialPropertyIndex enableShader = material->FindPropertyIndex(Name{"EnableSecondShader"});
  557. const ShaderCollection& shaderCollection = material->GetGeneralShaderCollection();
  558. EXPECT_TRUE(shaderCollection[0].IsEnabled());
  559. EXPECT_FALSE(shaderCollection[1].IsEnabled());
  560. EXPECT_TRUE(shaderCollection[2].IsEnabled());
  561. material->SetPropertyValue(enableShader, true);
  562. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  563. material->Compile();
  564. EXPECT_TRUE(shaderCollection[0].IsEnabled());
  565. EXPECT_TRUE(shaderCollection[1].IsEnabled());
  566. EXPECT_TRUE(shaderCollection[2].IsEnabled());
  567. material->SetPropertyValue(enableShader, false);
  568. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  569. material->Compile();
  570. EXPECT_TRUE(shaderCollection[0].IsEnabled());
  571. EXPECT_FALSE(shaderCollection[1].IsEnabled());
  572. EXPECT_TRUE(shaderCollection[2].IsEnabled());
  573. }
  574. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToInternalProperty_ConnectedToShaderEnabled)
  575. {
  576. // This tests the concept of internal properties used to pass data from the material to a MaterialPipelinePayload.
  577. // The test will set up a structure like this:
  578. // Properties
  579. // "general.useSpecialFeature" connects to "EnableSpecialFeature" in each pipeline
  580. // Material pipelines
  581. // "PipalineA"
  582. // Properties
  583. // "EnableSpecialFeature" connects to enable the local "special" shader
  584. // Shaders
  585. // "shader1"
  586. // "shader2"
  587. // "special"
  588. // "PipalineB"
  589. // Properties
  590. // "EnableSpecialFeature" connects to enable the local "peculiar" shader
  591. // Shaders
  592. // "shaderA"
  593. // "peculiar"
  594. // "shaderB"
  595. MaterialTypeAssetCreator materialTypeCreator;
  596. materialTypeCreator.Begin(Uuid::CreateRandom());
  597. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shader1"}, Name{"PipalineA"});
  598. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shader2"}, Name{"PipalineA"});
  599. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"special"}, Name{"PipalineA"});
  600. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shaderA"}, Name{"PipalineB"});
  601. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"peculiar"}, Name{"PipalineB"});
  602. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shaderB"}, Name{"PipalineB"});
  603. // PipalineA's EnableSpecialFeature connects to its "special" shader
  604. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineA"});
  605. materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"special"});
  606. materialTypeCreator.EndMaterialProperty();
  607. // PipalineB's EnableSpecialFeature connects to its "peculiar" shader
  608. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineB"});
  609. materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"peculiar"});
  610. materialTypeCreator.EndMaterialProperty();
  611. // The main property connects to EnableSpecialFeature
  612. materialTypeCreator.BeginMaterialProperty(Name{"general.useSpecialFeature"}, MaterialPropertyDataType::Bool, MaterialPipelineNone);
  613. materialTypeCreator.ConnectMaterialPropertyToInternalProperty(Name{"EnableSpecialFeature"});
  614. materialTypeCreator.EndMaterialProperty();
  615. materialTypeCreator.End(m_testMaterialTypeAsset);
  616. MaterialAssetCreator materialAssetCreator;
  617. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  618. materialAssetCreator.End(m_testMaterialAsset);
  619. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  620. MaterialPropertyIndex enableShader = material->FindPropertyIndex(Name{"general.useSpecialFeature"});
  621. const ShaderCollection& shaderCollectionA = material->GetShaderCollection(Name{"PipalineA"});
  622. const ShaderCollection& shaderCollectionB = material->GetShaderCollection(Name{"PipalineB"});
  623. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  624. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  625. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  626. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  627. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  628. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  629. material->SetPropertyValue(enableShader, true);
  630. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  631. material->Compile();
  632. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  633. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  634. EXPECT_TRUE(shaderCollectionA[2].IsEnabled());
  635. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  636. EXPECT_TRUE(shaderCollectionB[1].IsEnabled());
  637. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  638. material->SetPropertyValue(enableShader, false);
  639. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  640. material->Compile();
  641. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  642. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  643. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  644. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  645. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  646. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  647. }
  648. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToMaterialFunctor_ConnectedToShaderEnabled)
  649. {
  650. using namespace AZ::RPI;
  651. // This is the same as TestSetPropertyValue_ConnectedToInternalProperty_ConnectedToShaderEnabled, except
  652. // a functor is used for the first connection instead of a direct connection.
  653. // The test will set up a structure like this:
  654. // Properties
  655. // "general.useSpecialFeature" doesn't connect to anything directly
  656. // Functors
  657. // SetInternalPropertyFunctor connects "general.useSpecialFeature" to "EnableSpecialFeature", which propagates to each pipeline
  658. // Material pipelines (This is the same as TestSetPropertyValue_ConnectedToInternalProperty_ConnectedToShaderEnabled)
  659. // "PipalineA"
  660. // Properties
  661. // "EnableSpecialFeature" connects to enable the local "special" shader
  662. // Shaders
  663. // "shader1"
  664. // "shader2"
  665. // "special"
  666. // "PipalineB"
  667. // Properties
  668. // "EnableSpecialFeature" connects to enable the local "peculiar" shader
  669. // Shaders
  670. // "shaderA"
  671. // "peculiar"
  672. // "shaderB"
  673. MaterialTypeAssetCreator materialTypeCreator;
  674. materialTypeCreator.Begin(Uuid::CreateRandom());
  675. materialTypeCreator.AddShader(m_testMaterialShaderAsset, ShaderVariantId{}, Name{"shader1"}, Name{"PipalineA"});
  676. materialTypeCreator.AddShader(m_testMaterialShaderAsset, ShaderVariantId{}, Name{"shader2"}, Name{"PipalineA"});
  677. materialTypeCreator.AddShader(m_testMaterialShaderAsset, ShaderVariantId{}, Name{"special"}, Name{"PipalineA"});
  678. materialTypeCreator.AddShader(m_testMaterialShaderAsset, ShaderVariantId{}, Name{"shaderA"}, Name{"PipalineB"});
  679. materialTypeCreator.AddShader(m_testMaterialShaderAsset, ShaderVariantId{}, Name{"peculiar"}, Name{"PipalineB"});
  680. materialTypeCreator.AddShader(m_testMaterialShaderAsset, ShaderVariantId{}, Name{"shaderB"}, Name{"PipalineB"});
  681. // PipalineA's EnableSpecialFeature connects to its "special" shader
  682. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineA"});
  683. materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"special"});
  684. materialTypeCreator.EndMaterialProperty();
  685. // PipalineB's EnableSpecialFeature connects to its "peculiar" shader
  686. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineB"});
  687. materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"peculiar"});
  688. materialTypeCreator.EndMaterialProperty();
  689. // The main property doesn't connect directly, because a functor is used
  690. materialTypeCreator.BeginMaterialProperty(Name{"general.useSpecialFeature"}, MaterialPropertyDataType::Bool, MaterialPipelineNone);
  691. materialTypeCreator.EndMaterialProperty();
  692. // This functor makes the connection between general.useSpecialFeature and EnableSpecialFeature
  693. SetInternalPropertyFunctorSourceData functorCreator;
  694. functorCreator.m_inputPropertyName = Name{"general.useSpecialFeature"};
  695. functorCreator.m_outputPropertyName = Name{"EnableSpecialFeature"};
  696. MaterialNameContext nameContext;
  697. MaterialFunctorSourceData::FunctorResult result = functorCreator.CreateFunctor(MaterialFunctorSourceData::RuntimeContext{
  698. "",
  699. materialTypeCreator.GetMaterialPropertiesLayout(),
  700. nullptr,
  701. &nameContext});
  702. materialTypeCreator.AddMaterialFunctor(result.GetValue(), MaterialPipelineNone);
  703. materialTypeCreator.End(m_testMaterialTypeAsset);
  704. MaterialAssetCreator materialAssetCreator;
  705. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  706. materialAssetCreator.End(m_testMaterialAsset);
  707. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  708. MaterialPropertyIndex enableShader = material->FindPropertyIndex(Name{"general.useSpecialFeature"});
  709. const ShaderCollection& shaderCollectionA = material->GetShaderCollection(Name{"PipalineA"});
  710. const ShaderCollection& shaderCollectionB = material->GetShaderCollection(Name{"PipalineB"});
  711. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  712. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  713. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  714. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  715. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  716. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  717. material->SetPropertyValue(enableShader, true);
  718. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  719. material->Compile();
  720. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  721. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  722. EXPECT_TRUE(shaderCollectionA[2].IsEnabled());
  723. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  724. EXPECT_TRUE(shaderCollectionB[1].IsEnabled());
  725. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  726. material->SetPropertyValue(enableShader, false);
  727. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  728. material->Compile();
  729. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  730. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  731. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  732. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  733. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  734. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  735. }
  736. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToInternalProperty_ConnectedToMaterialPipelineFunctor)
  737. {
  738. // This is the same as TestSetPropertyValue_ConnectedToInternalProperty_ConnectedToShaderEnabled, except
  739. // a functor is used for the final ShaderEnable connection instead of a direct connection.
  740. // The test will set up a structure like this:
  741. // Properties
  742. // "general.useSpecialFeature" connects to "EnableSpecialFeature" in each pipeline
  743. // Material pipelines
  744. // "PipalineA"
  745. // Properties
  746. // "EnableSpecialFeature" doesn't connect to anything directly
  747. // Shaders
  748. // "shader1"
  749. // "shader2"
  750. // "special"
  751. // Functors
  752. // ShaderEnablePipelineFunctor uses "EnableSpecialFeature" to enable/disable the "special" shader
  753. // "PipalineB"
  754. // Properties
  755. // "EnableSpecialFeature" doesn't connect to anything directly
  756. // Shaders
  757. // "shaderA"
  758. // "peculiar"
  759. // "shaderB"
  760. // Functors
  761. // ShaderEnablePipelineFunctor uses "EnableSpecialFeature" to enable/disable the "peculiar" shader
  762. MaterialTypeAssetCreator materialTypeCreator;
  763. materialTypeCreator.Begin(Uuid::CreateRandom());
  764. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shader1"}, Name{"PipalineA"});
  765. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shader2"}, Name{"PipalineA"});
  766. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"special"}, Name{"PipalineA"});
  767. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shaderA"}, Name{"PipalineB"});
  768. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"peculiar"}, Name{"PipalineB"});
  769. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shaderB"}, Name{"PipalineB"});
  770. // PipalineA's EnableSpecialFeature connects to its "special" shader
  771. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineA"});
  772. materialTypeCreator.EndMaterialProperty();
  773. // This functor makes the connection between EnableSpecialFeature and PipelineA's "special" shader
  774. ShaderEnablePipelineFunctorSourceData shaderEnableFunctorCreator;
  775. shaderEnableFunctorCreator.m_enablePropertyName = Name{"EnableSpecialFeature"};
  776. shaderEnableFunctorCreator.m_shaderTag = Name{"special"};
  777. MaterialNameContext defaultNameContext;
  778. MaterialFunctorSourceData::FunctorResult createFunctorResult = shaderEnableFunctorCreator.CreateFunctor(MaterialFunctorSourceData::RuntimeContext{
  779. "",
  780. materialTypeCreator.GetMaterialPropertiesLayout(Name{"PipalineA"}),
  781. nullptr,
  782. & defaultNameContext});
  783. materialTypeCreator.AddMaterialFunctor(createFunctorResult.GetValue(), Name{"PipalineA"});
  784. // PipalineB's EnableSpecialFeature connects to its "peculiar" shader
  785. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineB"});
  786. materialTypeCreator.EndMaterialProperty();
  787. // This functor makes the connection between EnableSpecialFeature and PipelineB's "peculiar" shader
  788. shaderEnableFunctorCreator.m_enablePropertyName = Name{"EnableSpecialFeature"};
  789. shaderEnableFunctorCreator.m_shaderTag = Name{"peculiar"};
  790. createFunctorResult = shaderEnableFunctorCreator.CreateFunctor(MaterialFunctorSourceData::RuntimeContext{
  791. "",
  792. materialTypeCreator.GetMaterialPropertiesLayout(Name{"PipalineB"}),
  793. nullptr,
  794. & defaultNameContext});
  795. materialTypeCreator.AddMaterialFunctor(createFunctorResult.GetValue(), Name{"PipalineB"});
  796. // The main property connects to EnableSpecialFeature
  797. materialTypeCreator.BeginMaterialProperty(Name{"general.useSpecialFeature"}, MaterialPropertyDataType::Bool, MaterialPipelineNone);
  798. materialTypeCreator.ConnectMaterialPropertyToInternalProperty(Name{"EnableSpecialFeature"});
  799. materialTypeCreator.EndMaterialProperty();
  800. materialTypeCreator.End(m_testMaterialTypeAsset);
  801. MaterialAssetCreator materialAssetCreator;
  802. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  803. materialAssetCreator.End(m_testMaterialAsset);
  804. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  805. MaterialPropertyIndex enableShader = material->FindPropertyIndex(Name{"general.useSpecialFeature"});
  806. const ShaderCollection& shaderCollectionA = material->GetShaderCollection(Name{"PipalineA"});
  807. const ShaderCollection& shaderCollectionB = material->GetShaderCollection(Name{"PipalineB"});
  808. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  809. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  810. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  811. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  812. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  813. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  814. material->SetPropertyValue(enableShader, true);
  815. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  816. material->Compile();
  817. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  818. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  819. EXPECT_TRUE(shaderCollectionA[2].IsEnabled());
  820. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  821. EXPECT_TRUE(shaderCollectionB[1].IsEnabled());
  822. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  823. material->SetPropertyValue(enableShader, false);
  824. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  825. material->Compile();
  826. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  827. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  828. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  829. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  830. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  831. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  832. }
  833. TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToMaterialFunctor_ConnectedToMaterialPipelineFunctor)
  834. {
  835. // This is the same as TestSetPropertyValue_ConnectedToInternalProperty_ConnectedToShaderEnabled, except
  836. // functors are used to connect everything instead of direct connections.
  837. // The test will set up a structure like this:
  838. // Properties
  839. // "general.useSpecialFeature" doesn't connect to anything directly
  840. // Functors
  841. // SetInternalPropertyFunctor connects "general.useSpecialFeature" to "EnableSpecialFeature", which propagates to each pipeline
  842. // Material pipelines
  843. // "PipalineA"
  844. // Properties
  845. // "EnableSpecialFeature" doesn't connect to anything directly
  846. // Shaders
  847. // "shader1"
  848. // "shader2"
  849. // "special"
  850. // Functors
  851. // ShaderEnablePipelineFunctor uses "EnableSpecialFeature" to enable/disable the "special" shader
  852. // "PipalineB"
  853. // Properties
  854. // "EnableSpecialFeature" doesn't connect to anything directly
  855. // Shaders
  856. // "shaderA"
  857. // "peculiar"
  858. // "shaderB"
  859. // Functors
  860. // ShaderEnablePipelineFunctor uses "EnableSpecialFeature" to enable/disable the "peculiar" shader
  861. MaterialTypeAssetCreator materialTypeCreator;
  862. materialTypeCreator.Begin(Uuid::CreateRandom());
  863. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shader1"}, Name{"PipalineA"});
  864. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shader2"}, Name{"PipalineA"});
  865. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"special"}, Name{"PipalineA"});
  866. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shaderA"}, Name{"PipalineB"});
  867. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"peculiar"}, Name{"PipalineB"});
  868. materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"shaderB"}, Name{"PipalineB"});
  869. // PipalineA's EnableSpecialFeature connects to its "special" shader
  870. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineA"});
  871. materialTypeCreator.EndMaterialProperty();
  872. // This functor makes the connection between EnableSpecialFeature and PipelineA's "special" shader
  873. ShaderEnablePipelineFunctorSourceData shaderEnableFunctorCreator;
  874. shaderEnableFunctorCreator.m_enablePropertyName = Name{"EnableSpecialFeature"};
  875. shaderEnableFunctorCreator.m_shaderTag = Name{"special"};
  876. MaterialNameContext defaultNameContext;
  877. MaterialFunctorSourceData::FunctorResult createFunctorResult = shaderEnableFunctorCreator.CreateFunctor(MaterialFunctorSourceData::RuntimeContext{
  878. "",
  879. materialTypeCreator.GetMaterialPropertiesLayout(Name{"PipalineA"}),
  880. nullptr,
  881. &defaultNameContext});
  882. materialTypeCreator.AddMaterialFunctor(createFunctorResult.GetValue(), Name{"PipalineA"});
  883. // PipalineB's EnableSpecialFeature connects to its "peculiar" shader
  884. materialTypeCreator.BeginMaterialProperty(Name{"EnableSpecialFeature"}, MaterialPropertyDataType::Bool, Name{"PipalineB"});
  885. materialTypeCreator.EndMaterialProperty();
  886. // This functor makes the connection between EnableSpecialFeature and PipelineB's "peculiar" shader
  887. shaderEnableFunctorCreator.m_enablePropertyName = Name{"EnableSpecialFeature"};
  888. shaderEnableFunctorCreator.m_shaderTag = Name{"peculiar"};
  889. createFunctorResult = shaderEnableFunctorCreator.CreateFunctor(MaterialFunctorSourceData::RuntimeContext{
  890. "",
  891. materialTypeCreator.GetMaterialPropertiesLayout(Name{"PipalineB"}),
  892. nullptr,
  893. &defaultNameContext});
  894. materialTypeCreator.AddMaterialFunctor(createFunctorResult.GetValue(), Name{"PipalineB"});
  895. // The main property doesn't connect directly, because a functor is used
  896. materialTypeCreator.BeginMaterialProperty(Name{"general.useSpecialFeature"}, MaterialPropertyDataType::Bool, MaterialPipelineNone);
  897. materialTypeCreator.EndMaterialProperty();
  898. // This functor makes the connection between general.useSpecialFeature and EnableSpecialFeature
  899. SetInternalPropertyFunctorSourceData setInternalPropertyFunctorCreator;
  900. setInternalPropertyFunctorCreator.m_inputPropertyName = Name{"general.useSpecialFeature"};
  901. setInternalPropertyFunctorCreator.m_outputPropertyName = Name{"EnableSpecialFeature"};
  902. MaterialFunctorSourceData::FunctorResult result = setInternalPropertyFunctorCreator.CreateFunctor(MaterialFunctorSourceData::RuntimeContext{
  903. "",
  904. materialTypeCreator.GetMaterialPropertiesLayout(),
  905. nullptr,
  906. &defaultNameContext});
  907. materialTypeCreator.AddMaterialFunctor(result.GetValue(), MaterialPipelineNone);
  908. materialTypeCreator.End(m_testMaterialTypeAsset);
  909. MaterialAssetCreator materialAssetCreator;
  910. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  911. materialAssetCreator.End(m_testMaterialAsset);
  912. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  913. MaterialPropertyIndex enableShader = material->FindPropertyIndex(Name{"general.useSpecialFeature"});
  914. const ShaderCollection& shaderCollectionA = material->GetShaderCollection(Name{"PipalineA"});
  915. const ShaderCollection& shaderCollectionB = material->GetShaderCollection(Name{"PipalineB"});
  916. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  917. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  918. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  919. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  920. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  921. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  922. material->SetPropertyValue(enableShader, true);
  923. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  924. material->Compile();
  925. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  926. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  927. EXPECT_TRUE(shaderCollectionA[2].IsEnabled());
  928. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  929. EXPECT_TRUE(shaderCollectionB[1].IsEnabled());
  930. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  931. material->SetPropertyValue(enableShader, false);
  932. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  933. material->Compile();
  934. EXPECT_TRUE(shaderCollectionA[0].IsEnabled());
  935. EXPECT_TRUE(shaderCollectionA[1].IsEnabled());
  936. EXPECT_FALSE(shaderCollectionA[2].IsEnabled());
  937. EXPECT_TRUE(shaderCollectionB[0].IsEnabled());
  938. EXPECT_FALSE(shaderCollectionB[1].IsEnabled());
  939. EXPECT_TRUE(shaderCollectionB[2].IsEnabled());
  940. }
  941. TEST_F(MaterialTests, TestSetSystemShaderOption)
  942. {
  943. Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
  944. Data::Asset<ShaderAsset> shaderAsset =
  945. CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
  946. MaterialTypeAssetCreator materialTypeCreator;
  947. materialTypeCreator.Begin(Uuid::CreateRandom());
  948. materialTypeCreator.AddShader(shaderAsset);
  949. materialTypeCreator.AddShader(shaderAsset);
  950. materialTypeCreator.AddShader(shaderAsset);
  951. materialTypeCreator.BeginMaterialProperty(Name{"BoolValue"}, MaterialPropertyDataType::Bool);
  952. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_boolA"}); // Applies to all shaders
  953. materialTypeCreator.EndMaterialProperty();
  954. materialTypeCreator.ClaimShaderOptionOwnership(Name{"o_boolB"});
  955. materialTypeCreator.End(m_testMaterialTypeAsset);
  956. MaterialAssetCreator materialAssetCreator;
  957. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  958. materialAssetCreator.End(m_testMaterialAsset);
  959. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  960. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_enumA"}, ShaderOptionValue{0}).GetValue());
  961. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_enumB"}, ShaderOptionValue{1}).GetValue());
  962. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_enumC"}, ShaderOptionValue{2}).GetValue());
  963. EXPECT_FALSE(material->SetSystemShaderOption(Name{"o_boolA"}, ShaderOptionValue{1}).IsSuccess());
  964. EXPECT_FALSE(material->SetSystemShaderOption(Name{"o_boolB"}, ShaderOptionValue{1}).IsSuccess());
  965. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_boolC"}, ShaderOptionValue{1}).GetValue());
  966. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_rangeA"}, ShaderOptionValue{3}).GetValue());
  967. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_rangeB"}, ShaderOptionValue{4}).GetValue());
  968. EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_rangeC"}, ShaderOptionValue{5}).GetValue());
  969. // Try setting a shader option that does not exist in this material
  970. auto result = material->SetSystemShaderOption(Name{"o_someOtherOption"}, ShaderOptionValue{1});
  971. EXPECT_TRUE(result.IsSuccess());
  972. EXPECT_EQ(0, result.GetValue());
  973. for (size_t i = 0; i < material->GetGeneralShaderCollection().size(); ++i)
  974. {
  975. auto& shaderItem = material->GetGeneralShaderCollection()[i];
  976. EXPECT_EQ(0, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumA"}).GetIndex());
  977. EXPECT_EQ(1, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumB"}).GetIndex());
  978. EXPECT_EQ(2, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumC"}).GetIndex());
  979. EXPECT_EQ(1, shaderItem.GetShaderOptions()->GetValue(Name{"o_boolC"}).GetIndex());
  980. EXPECT_EQ(3, shaderItem.GetShaderOptions()->GetValue(Name{"o_rangeA"}).GetIndex());
  981. EXPECT_EQ(4, shaderItem.GetShaderOptions()->GetValue(Name{"o_rangeB"}).GetIndex());
  982. EXPECT_EQ(5, shaderItem.GetShaderOptions()->GetValue(Name{"o_rangeC"}).GetIndex());
  983. // We don't care whether a material-owned shader option is unspecified or is initialized to its default state.
  984. // The important thing is that it did not change from its default value.
  985. auto checkValueNotChanged = [&shaderItem](const Name& name, ShaderOptionValue expectedValue)
  986. {
  987. ShaderOptionValue value = shaderItem.GetShaderOptions()->GetValue(name);
  988. if (value.IsValid())
  989. {
  990. EXPECT_EQ(expectedValue.GetIndex(), value.GetIndex());
  991. }
  992. };
  993. checkValueNotChanged(Name{"o_boolA"}, ShaderOptionValue{0});
  994. checkValueNotChanged(Name{"o_boolB"}, ShaderOptionValue{0});
  995. }
  996. }
  997. TEST_F(MaterialTests, Error_InvalidShaderOptionValue)
  998. {
  999. Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
  1000. Data::Asset<ShaderAsset> shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
  1001. MaterialTypeAssetCreator materialTypeCreator;
  1002. materialTypeCreator.Begin(Uuid::CreateRandom());
  1003. materialTypeCreator.AddShader(shaderAsset);
  1004. materialTypeCreator.BeginMaterialProperty(Name{"Value"}, MaterialPropertyDataType::Int);
  1005. materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_rangeA"});
  1006. materialTypeCreator.EndMaterialProperty();
  1007. materialTypeCreator.SetPropertyValue(Name{"Value"}, 1);
  1008. materialTypeCreator.End(m_testMaterialTypeAsset);
  1009. MaterialAssetCreator materialAssetCreator;
  1010. materialAssetCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  1011. materialAssetCreator.End(m_testMaterialAsset);
  1012. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  1013. ErrorMessageFinder errorMessageFinder("ShaderOption value [100] is out of range");
  1014. // Depending on implementation, the error message might not actually be reported until Compile() is called.
  1015. // In that case, SetPropertyValue won't know about any issue and just return true after setting the property value.
  1016. // If we want SetPropertyValue to return false, then we would have to change the implementation to process
  1017. // property connections immediately instead of in Compile(), but there's little point to that as lua functors
  1018. // can't be processed immediately, they have to wait for Compile(). So we might as well keep all our back-end
  1019. // error checking in Compile().
  1020. material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"Value"}), 100); // 100 is out of range, max is 10
  1021. ProcessQueuedSrgCompilations(shaderAsset, m_testMaterialSrgLayout->GetName());
  1022. material->Compile();
  1023. errorMessageFinder.CheckExpectedErrorsFound();
  1024. }
  1025. TEST_F(MaterialTests, Error_ImageNotFound)
  1026. {
  1027. Data::Asset<MaterialAsset> materialAsset;
  1028. MaterialAssetCreator materialCreator;
  1029. materialCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  1030. materialCreator.SetPropertyValue(Name{ "MyFloat2" }, Vector2{ 0.1f, 0.2f });
  1031. materialCreator.SetPropertyValue(Name{ "MyFloat3" }, Vector3{ 1.1f, 1.2f, 1.3f });
  1032. materialCreator.SetPropertyValue(Name{ "MyFloat4" }, Vector4{ 2.1f, 2.2f, 2.3f, 2.4f });
  1033. materialCreator.SetPropertyValue(Name{ "MyColor" }, Color{ 1.0f, 1.0f, 1.0f, 1.0f });
  1034. materialCreator.SetPropertyValue(Name{ "MyInt" }, -2);
  1035. materialCreator.SetPropertyValue(Name{ "MyUInt" }, 12u);
  1036. materialCreator.SetPropertyValue(Name{ "MyFloat" }, 1.5f);
  1037. materialCreator.SetPropertyValue(Name{ "MyBool" }, true);
  1038. // Set the image to an empty asset handle that isn't associated with any actual data. StreamingImage::FindOrCreate will fail.
  1039. materialCreator.SetPropertyValue(Name{ "MyImage" }, Data::Asset<ImageAsset>(Uuid::CreateRandom(), azrtti_typeid<StreamingImageAsset>()));
  1040. materialCreator.End(materialAsset);
  1041. ErrorMessageFinder errorMessageFinder{"Image asset could not be loaded"};
  1042. // The material may trigger a blocking load of the image asset, but there is no catalog in unit tests.
  1043. errorMessageFinder.AddIgnoredErrorMessage("this type doesn't have a catalog", true);
  1044. errorMessageFinder.AddIgnoredErrorMessage("Failed to retrieve required information for asset", true);
  1045. errorMessageFinder.AddIgnoredErrorMessage("GetAsset called for asset which does not exist", true);
  1046. Data::Instance<Material> material = Material::FindOrCreate(materialAsset);
  1047. errorMessageFinder.CheckExpectedErrorsFound();
  1048. EXPECT_EQ(nullptr, material);
  1049. }
  1050. TEST_F(MaterialTests, Error_AccessInvalidProperty)
  1051. {
  1052. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  1053. AZ_TEST_START_ASSERTTEST;
  1054. EXPECT_FALSE(material->SetPropertyValue<float>(MaterialPropertyIndex(), 0.0f));
  1055. material->GetPropertyValue<float>(MaterialPropertyIndex());
  1056. AZ_TEST_STOP_ASSERTTEST(2);
  1057. }
  1058. TEST_F(MaterialTests, Error_SetPropertyValue_WrongDataType)
  1059. {
  1060. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  1061. {
  1062. ErrorMessageFinder finder;
  1063. finder.AddExpectedErrorMessage("Accessed as type", 9);
  1064. finder.AddExpectedErrorMessage("but is type", 9);
  1065. EXPECT_FALSE(material->SetPropertyValue<bool>(material->FindPropertyIndex(Name{"MyImage"}), false));
  1066. EXPECT_FALSE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"MyBool"}), -5));
  1067. EXPECT_FALSE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"MyInt"}), 123u));
  1068. EXPECT_FALSE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{"MyUInt"}), 2.5f));
  1069. EXPECT_FALSE(material->SetPropertyValue<Vector2>(material->FindPropertyIndex(Name{"MyFloat"}), Vector2(10.1f, 10.2f)));
  1070. EXPECT_FALSE(material->SetPropertyValue<Vector3>(material->FindPropertyIndex(Name{"MyFloat2"}), Vector3(11.1f, 11.2f, 11.3f)));
  1071. EXPECT_FALSE(material->SetPropertyValue<Vector4>(material->FindPropertyIndex(Name{"MyFloat3"}), Vector4(12.1f, 12.2f, 12.3f, 12.4f)));
  1072. EXPECT_FALSE(material->SetPropertyValue<Color>(material->FindPropertyIndex(Name{"MyFloat4"}), Color(0.1f, 0.2f, 0.3f, 0.4f)));
  1073. EXPECT_FALSE(material->SetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{"MyColor"}), m_testImage));
  1074. finder.CheckExpectedErrorsFound();
  1075. }
  1076. // Make sure the values have not changed
  1077. ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
  1078. material->Compile();
  1079. ValidateInitialValuesFromMaterial(material);
  1080. }
  1081. TEST_F(MaterialTests, Error_GetPropertyValue_WrongDataType)
  1082. {
  1083. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  1084. ErrorMessageFinder finder;
  1085. finder.AddExpectedErrorMessage("Accessed as type", 9);
  1086. finder.AddExpectedErrorMessage("but is type", 9);
  1087. material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyImage" }));
  1088. material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyBool" }));
  1089. material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyInt" }));
  1090. material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyUInt" }));
  1091. material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat" }));
  1092. material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat2" }));
  1093. material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat3" }));
  1094. material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyFloat4" }));
  1095. material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyColor" }));
  1096. finder.CheckExpectedErrorsFound();
  1097. }
  1098. TEST_F(MaterialTests, ColorPropertyCanMapToFloat3)
  1099. {
  1100. Data::Asset<MaterialTypeAsset> materialTypeAsset;
  1101. Data::Asset<MaterialAsset> materialAsset;
  1102. RHI::Ptr<RHI::ShaderResourceGroupLayout> srgLayout = RHI::ShaderResourceGroupLayout::Create();
  1103. srgLayout->SetName(Name("MaterialSrg"));
  1104. srgLayout->SetBindingSlot(SrgBindingSlot::Material);
  1105. srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name{"m_color"}, 0, 12, 0, 0});
  1106. ASSERT_TRUE(srgLayout->Finalize());
  1107. Data::Asset<ShaderAsset> shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), srgLayout);
  1108. MaterialTypeAssetCreator materialTypeCreator;
  1109. materialTypeCreator.Begin(Uuid::CreateRandom());
  1110. materialTypeCreator.AddShader(shaderAsset);
  1111. materialTypeCreator.BeginMaterialProperty(Name{ "MyColor" }, MaterialPropertyDataType::Color);
  1112. materialTypeCreator.ConnectMaterialPropertyToShaderInput(Name{ "m_color" });
  1113. materialTypeCreator.EndMaterialProperty();
  1114. materialTypeCreator.End(materialTypeAsset);
  1115. MaterialAssetCreator materialAssetCreator;
  1116. materialAssetCreator.Begin(Uuid::CreateRandom(), materialTypeAsset);
  1117. materialAssetCreator.End(materialAsset);
  1118. Data::Instance<Material> material = Material::FindOrCreate(materialAsset);
  1119. const AZ::Color inputColor{1.0f, 2.0f, 3.0f, 0.0f};
  1120. MaterialPropertyIndex colorProperty{0};
  1121. material->SetPropertyValue(colorProperty, inputColor);
  1122. ProcessQueuedSrgCompilations(shaderAsset, srgLayout->GetName());
  1123. material->Compile();
  1124. AZ::Color colorFromMaterial = material->GetPropertyValue<AZ::Color>(colorProperty);
  1125. RHI::ShaderInputConstantIndex colorConstant{0};
  1126. AZ::Color colorFromSrg;
  1127. const float* floatsFromSrg = reinterpret_cast<const float*>(material->GetRHIShaderResourceGroup()->GetData().GetConstantRaw(colorConstant).data());
  1128. colorFromSrg = AZ::Color::CreateFromVector3(AZ::Vector3::CreateFromFloat3(floatsFromSrg));
  1129. for (int i = 0; i < 3; ++i)
  1130. {
  1131. EXPECT_EQ((float)TransformColor(inputColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg).GetElement(i), (float)colorFromSrg.GetElement(i));
  1132. EXPECT_EQ((float)inputColor.GetElement(i), (float)colorFromMaterial.GetElement(i));
  1133. }
  1134. }
  1135. TEST_F(MaterialTests, TestFindPropertyIndexUsingOldName)
  1136. {
  1137. MaterialTypeAssetCreator materialTypeCreator;
  1138. materialTypeCreator.Begin(Uuid::CreateRandom());
  1139. materialTypeCreator.AddShader(m_testMaterialShaderAsset);
  1140. AddCommonTestMaterialProperties(materialTypeCreator);
  1141. materialTypeCreator.SetVersion(2);
  1142. MaterialVersionUpdate versionUpdate(2);
  1143. versionUpdate.AddAction(MaterialVersionUpdate::Action(Name("rename"),
  1144. {
  1145. { Name{ "from" }, AZStd::string("OldName") },
  1146. { Name{ "to" }, AZStd::string("MyInt") }
  1147. } ));
  1148. materialTypeCreator.AddVersionUpdate(versionUpdate);
  1149. materialTypeCreator.End(m_testMaterialTypeAsset);
  1150. MaterialAssetCreator materialCreator;
  1151. materialCreator.Begin(Uuid::CreateRandom(), m_testMaterialTypeAsset);
  1152. materialCreator.End(m_testMaterialAsset);
  1153. Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
  1154. bool wasRenamed = false;
  1155. Name newName;
  1156. MaterialPropertyIndex indexFromOldName = material->FindPropertyIndex(Name{"OldName"}, &wasRenamed, &newName);
  1157. EXPECT_TRUE(wasRenamed);
  1158. EXPECT_EQ(newName, Name{"MyInt"});
  1159. MaterialPropertyIndex indexFromNewName = material->FindPropertyIndex(Name{"MyInt"}, &wasRenamed, &newName);
  1160. EXPECT_FALSE(wasRenamed);
  1161. EXPECT_EQ(indexFromOldName, indexFromNewName);
  1162. }
  1163. template<typename T>
  1164. void CheckPropertyValueRoundTrip(const T& value)
  1165. {
  1166. AZ::RPI::MaterialPropertyValue materialPropertyValue{value};
  1167. AZStd::any anyValue{value};
  1168. AZ::RPI::MaterialPropertyValue materialPropertyValueFromAny = MaterialPropertyValue::FromAny(anyValue);
  1169. AZ::RPI::MaterialPropertyValue materialPropertyValueFromRoundTrip = MaterialPropertyValue::FromAny(MaterialPropertyValue::ToAny(materialPropertyValue));
  1170. EXPECT_EQ(materialPropertyValue, materialPropertyValueFromAny);
  1171. EXPECT_EQ(materialPropertyValue, materialPropertyValueFromRoundTrip);
  1172. if (materialPropertyValue.Is<Data::Asset<ImageAsset>>())
  1173. {
  1174. EXPECT_EQ(materialPropertyValue.GetValue<Data::Asset<ImageAsset>>().GetHint(), materialPropertyValueFromAny.GetValue<Data::Asset<ImageAsset>>().GetHint());
  1175. EXPECT_EQ(materialPropertyValue.GetValue<Data::Asset<ImageAsset>>().GetHint(), materialPropertyValueFromRoundTrip.GetValue<Data::Asset<ImageAsset>>().GetHint());
  1176. }
  1177. }
  1178. TEST_F(MaterialTests, TestMaterialPropertyValueAsAny)
  1179. {
  1180. CheckPropertyValueRoundTrip(true);
  1181. CheckPropertyValueRoundTrip(false);
  1182. CheckPropertyValueRoundTrip(7);
  1183. CheckPropertyValueRoundTrip(8u);
  1184. CheckPropertyValueRoundTrip(9.0f);
  1185. CheckPropertyValueRoundTrip(AZ::Vector2(1.0f, 2.0f));
  1186. CheckPropertyValueRoundTrip(AZ::Vector3(1.0f, 2.0f, 3.0f));
  1187. CheckPropertyValueRoundTrip(AZ::Vector4(1.0f, 2.0f, 3.0f, 4.0f));
  1188. CheckPropertyValueRoundTrip(AZ::Color(1.0f, 2.0f, 3.0f, 4.0f));
  1189. CheckPropertyValueRoundTrip(Data::Asset<Data::AssetData>{});
  1190. CheckPropertyValueRoundTrip(Data::Asset<ImageAsset>{});
  1191. CheckPropertyValueRoundTrip(Data::Asset<StreamingImageAsset>{});
  1192. CheckPropertyValueRoundTrip(Data::Asset<AttachmentImageAsset>{});
  1193. CheckPropertyValueRoundTrip(Data::Asset<Data::AssetData>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), "TestAssetPath.png"});
  1194. CheckPropertyValueRoundTrip(Data::Asset<Data::AssetData>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::AttachmentImageAsset>(), "TestAssetPath.attimage"});
  1195. CheckPropertyValueRoundTrip(Data::Asset<ImageAsset>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), "TestAssetPath.png"});
  1196. CheckPropertyValueRoundTrip(Data::Asset<ImageAsset>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::AttachmentImageAsset>(), "TestAssetPath.attimage"});
  1197. CheckPropertyValueRoundTrip(Data::Asset<StreamingImageAsset>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), "TestAssetPath.png"});
  1198. CheckPropertyValueRoundTrip(Data::Asset<AttachmentImageAsset>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::AttachmentImageAsset>(), "TestAssetPath.attimage"});
  1199. CheckPropertyValueRoundTrip(m_testImageAsset);
  1200. CheckPropertyValueRoundTrip(m_testAttachmentImageAsset);
  1201. CheckPropertyValueRoundTrip(Data::Instance<Image>{m_testImage});
  1202. CheckPropertyValueRoundTrip(Data::Instance<Image>{m_testAttachmentImage});
  1203. CheckPropertyValueRoundTrip(AZStd::string{"hello"});
  1204. }
  1205. }