ShaderTests.cpp 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065
  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 <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  10. #include <Atom/RHI.Reflect/ShaderStageFunction.h>
  11. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  12. #include <Atom/RPI.Reflect/Shader/ShaderAssetCreator.h>
  13. #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
  14. #include <Atom/RPI.Edit/Shader/ShaderVariantTreeAssetCreator.h>
  15. #include <Atom/RPI.Edit/Shader/ShaderVariantAssetCreator.h>
  16. #include <Atom/RHI/RHISystemInterface.h>
  17. #include <Atom/RPI.Public/Shader/Shader.h>
  18. #include <Common/RPITestFixture.h>
  19. #include <Common/ErrorMessageFinder.h>
  20. #include <Common/SerializeTester.h>
  21. #include <AzCore/Serialization/SerializeContext.h>
  22. #include <AzCore/Utils/TypeHash.h>
  23. #include <AzCore/Math/Random.h>
  24. #include <AzCore/std/string/conversions.h>
  25. namespace AZ
  26. {
  27. namespace RPI
  28. {
  29. /// This length represents the up-aligned shader variant key length in respect to the shader register space
  30. /// AZSLc aligns all keys up to a register length and this constant emulates that requirement
  31. static constexpr uint32_t ShaderVariantKeyAlignedBitCount = (ShaderVariantKeyBitCount % ShaderRegisterBitSize == 0) ?
  32. ShaderVariantKeyBitCount :
  33. ShaderVariantKeyBitCount + (ShaderRegisterBitSize - ShaderVariantKeyBitCount % ShaderRegisterBitSize);
  34. class ShaderAssetTester
  35. : public UnitTest::SerializeTester<ShaderAsset>
  36. {
  37. using Base = UnitTest::SerializeTester<ShaderAsset>;
  38. public:
  39. ShaderAssetTester(AZ::SerializeContext* serializeContext)
  40. : Base(serializeContext)
  41. {}
  42. AZ::Data::Asset<ShaderAsset> SerializeInHelper(const AZ::Data::AssetId& assetId)
  43. {
  44. AZ::Data::Asset<ShaderAsset> asset = Base::SerializeIn(assetId);
  45. asset->SelectShaderApiData();
  46. asset->SetReady();
  47. return asset;
  48. }
  49. };
  50. }
  51. }
  52. namespace UnitTest
  53. {
  54. using namespace AZ;
  55. using ShaderByteCode = AZStd::vector<uint8_t>;
  56. class TestPipelineLayoutDescriptor
  57. : public AZ::RHI::PipelineLayoutDescriptor
  58. {
  59. public:
  60. AZ_RTTI(TestPipelineLayoutDescriptor, "{B226636F-7C85-4500-B499-26C112D1128B}", AZ::RHI::PipelineLayoutDescriptor);
  61. static void Reflect(AZ::ReflectContext* context)
  62. {
  63. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  64. {
  65. serializeContext->Class<TestPipelineLayoutDescriptor, AZ::RHI::PipelineLayoutDescriptor>()
  66. ->Version(1)
  67. ;
  68. }
  69. }
  70. static AZ::RHI::Ptr<TestPipelineLayoutDescriptor> Create()
  71. {
  72. return aznew TestPipelineLayoutDescriptor;
  73. }
  74. };
  75. class TestShaderStageFunction
  76. : public AZ::RHI::ShaderStageFunction
  77. {
  78. public:
  79. AZ_RTTI(TestShaderStageFunction, "{1BAEE536-96CA-4AEB-BA73-D5D72EE35B45}", AZ::RHI::ShaderStageFunction);
  80. AZ_CLASS_ALLOCATOR(TestShaderStageFunction, AZ::SystemAllocator)
  81. static void Reflect(AZ::ReflectContext* context)
  82. {
  83. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  84. {
  85. serializeContext->Class<TestShaderStageFunction, AZ::RHI::ShaderStageFunction>()
  86. ->Version(1)
  87. ->Field("m_byteCode", &TestShaderStageFunction::m_byteCode)
  88. ->Field("m_index", &TestShaderStageFunction::m_index)
  89. ;
  90. }
  91. }
  92. TestShaderStageFunction() = default;
  93. explicit TestShaderStageFunction(AZ::RHI::ShaderStage shaderStage)
  94. : AZ::RHI::ShaderStageFunction(shaderStage)
  95. {}
  96. void SetIndex(uint32_t index)
  97. {
  98. m_index = index;
  99. }
  100. int32_t m_index;
  101. ShaderByteCode m_byteCode;
  102. private:
  103. AZ::RHI::ResultCode FinalizeInternal() override
  104. {
  105. SetHash(AZ::TypeHash64(reinterpret_cast<const uint8_t*>(m_byteCode.data()), m_byteCode.size()));
  106. return AZ::RHI::ResultCode::Success;
  107. }
  108. };
  109. class ShaderTests
  110. : public RPITestFixture
  111. {
  112. protected:
  113. enum class SpecializationType
  114. {
  115. None = 0,
  116. Partial,
  117. Full,
  118. Count
  119. };
  120. static const uint32_t SpecializationTypeCount = static_cast<uint32_t>(SpecializationType::Count);
  121. void SetUp() override
  122. {
  123. using namespace AZ;
  124. RPITestFixture::SetUp();
  125. auto* serializeContext = GetSerializeContext();
  126. TestPipelineLayoutDescriptor::Reflect(serializeContext);
  127. TestShaderStageFunction::Reflect(serializeContext);
  128. // Example of unscoped enum
  129. AZStd::vector<RPI::ShaderOptionValuePair> idList0;
  130. idList0.push_back({ Name("Black"), RPI::ShaderOptionValue(0) }); // 1+ bit
  131. idList0.push_back({ Name("Maroon"), RPI::ShaderOptionValue(1) }); // ...
  132. idList0.push_back({ Name("Green"), RPI::ShaderOptionValue(2) }); // 2+ bits
  133. idList0.push_back({ Name("Olive"), RPI::ShaderOptionValue(3) }); // ...
  134. idList0.push_back({ Name("Navy"), RPI::ShaderOptionValue(4) }); // 3+ bits
  135. idList0.push_back({ Name("Purple"), RPI::ShaderOptionValue(5) }); // ...
  136. idList0.push_back({ Name("Teal"), RPI::ShaderOptionValue(6) }); // ...
  137. idList0.push_back({ Name("Silver"), RPI::ShaderOptionValue(7) }); // ...
  138. idList0.push_back({ Name("Gray"), RPI::ShaderOptionValue(8) }); // 4+ bits
  139. idList0.push_back({ Name("Red"), RPI::ShaderOptionValue(9) }); // ...
  140. idList0.push_back({ Name("Lime"), RPI::ShaderOptionValue(10) }); // ...
  141. idList0.push_back({ Name("Yellow"), RPI::ShaderOptionValue(11) }); // ...
  142. idList0.push_back({ Name("Blue"), RPI::ShaderOptionValue(12) }); // ...
  143. idList0.push_back({ Name("Fuchsia"), RPI::ShaderOptionValue(13) }); // ...
  144. idList0.push_back({ Name("Cyan"), RPI::ShaderOptionValue(14) }); // ...
  145. idList0.push_back({ Name("White"), RPI::ShaderOptionValue(15) }); // ...
  146. uint32_t bitOffset = 0;
  147. uint32_t order = 0;
  148. m_bindings[0] = RPI::ShaderOptionDescriptor{ Name("Color"),
  149. RPI::ShaderOptionType::Enumeration,
  150. bitOffset,
  151. order++,
  152. idList0,
  153. Name("Fuchsia") };
  154. bitOffset = m_bindings[0].GetBitOffset() + m_bindings[0].GetBitCount();
  155. // Example of scoped enum - the only difference is that enumerators are qualified
  156. AZStd::vector<RPI::ShaderOptionValuePair> idList1;
  157. idList1.push_back({ Name("Quality::Auto"), RPI::ShaderOptionValue(0) }); // 1+ bit
  158. idList1.push_back({ Name("Quality::Poor"), RPI::ShaderOptionValue(1) }); // ...
  159. idList1.push_back({ Name("Quality::Low"), RPI::ShaderOptionValue(2) }); // 2+ bits
  160. idList1.push_back({ Name("Quality::Average"), RPI::ShaderOptionValue(3) }); // ...
  161. idList1.push_back({ Name("Quality::Good"), RPI::ShaderOptionValue(4) }); // 3+ bits
  162. idList1.push_back({ Name("Quality::High"), RPI::ShaderOptionValue(5) }); // ...
  163. idList1.push_back({ Name("Quality::Ultra"), RPI::ShaderOptionValue(6) }); // ...
  164. idList1.push_back({ Name("Quality::Sublime"), RPI::ShaderOptionValue(7) }); // ...
  165. m_bindings[1] = RPI::ShaderOptionDescriptor{ Name("Quality"),
  166. RPI::ShaderOptionType::Enumeration,
  167. bitOffset,
  168. order++,
  169. idList1,
  170. Name("Quality::Auto") };
  171. bitOffset = m_bindings[1].GetBitOffset() + m_bindings[1].GetBitCount();
  172. // Example of integer range. It only requires two values, min and max. The name id-s are expected to match the numericla value.
  173. AZStd::vector<RPI::ShaderOptionValuePair> idList2;
  174. idList2.push_back({ Name("5"), RPI::ShaderOptionValue(5) }); // 1+ bit
  175. idList2.push_back({ Name("200"), RPI::ShaderOptionValue(200) }); // 8+ bits
  176. idList2.push_back({ Name("10"), RPI::ShaderOptionValue(10) }); // It doesn't really matter whether there are extra numbers; the shader option will take the min and max
  177. m_bindings[2] = RPI::ShaderOptionDescriptor{ Name("NumberSamples"),
  178. RPI::ShaderOptionType::IntegerRange,
  179. bitOffset,
  180. order++,
  181. idList2,
  182. Name("50") };
  183. bitOffset = m_bindings[2].GetBitOffset() + m_bindings[2].GetBitCount();
  184. // Example of boolean. By standard, the first value should be false (0).
  185. AZStd::vector<RPI::ShaderOptionValuePair> idList3;
  186. idList3.push_back({ Name("Off"), RPI::ShaderOptionValue(0) }); // 1+ bit
  187. idList3.push_back({ Name("On"), RPI::ShaderOptionValue(1) }); // ...
  188. m_bindings[3] = RPI::ShaderOptionDescriptor{ Name("Raytracing"),
  189. RPI::ShaderOptionType::Boolean,
  190. bitOffset,
  191. order++,
  192. idList3,
  193. Name("Off") };
  194. bitOffset = m_bindings[3].GetBitOffset() + m_bindings[3].GetBitCount();
  195. AZStd::vector<RPI::ShaderOptionValuePair> idList4;
  196. idList4.push_back({ Name("True"), RPI::ShaderOptionValue(0) }); // 1+ bit
  197. idList4.push_back({ Name("False"), RPI::ShaderOptionValue(1) }); // ...
  198. for (uint32_t i = 0; i < m_bindingsFullSpecialization.size(); ++i)
  199. {
  200. m_bindingsFullSpecialization[i] = RPI::ShaderOptionDescriptor{
  201. Name{ AZStd::to_string(i) }, RPI::ShaderOptionType::Boolean, i, i, idList4, Name("True"), 0,
  202. aznumeric_caster(i) };
  203. }
  204. for (uint32_t i = 0; i < m_bindingsPartialSpecialization.size(); ++i)
  205. {
  206. m_bindingsPartialSpecialization[i] = RPI::ShaderOptionDescriptor{ Name{ AZStd::to_string(i) },
  207. RPI::ShaderOptionType::Boolean,
  208. i,
  209. i,
  210. idList4,
  211. Name("True"),
  212. 0,
  213. aznumeric_caster(i % 2) ? aznumeric_caster(i) : -1 };
  214. }
  215. m_name = Name("TestName");
  216. m_drawListName = Name("DrawListTagName");
  217. m_pipelineLayoutDescriptor = TestPipelineLayoutDescriptor::Create();
  218. m_shaderOptionGroupLayoutForAsset = CreateShaderOptionLayout();
  219. m_shaderOptionGroupLayoutForAssetPartialSpecialization = CreateShaderOptionLayout({}, SpecializationType::Partial);
  220. m_shaderOptionGroupLayoutForAssetFullSpecialization = CreateShaderOptionLayout({}, SpecializationType::Full);
  221. m_shaderOptionGroupLayoutForVariants = m_shaderOptionGroupLayoutForAsset;
  222. // Just set up a couple values, not the whole struct, for some basic checking later that the struct is copied.
  223. m_renderStates.m_rasterState.m_fillMode = RHI::FillMode::Wireframe;
  224. m_renderStates.m_multisampleState.m_samples = 4;
  225. m_renderStates.m_depthStencilState.m_depth.m_func = RHI::ComparisonFunc::Equal;
  226. m_renderStates.m_depthStencilState.m_stencil.m_enable = 1;
  227. m_renderStates.m_blendState.m_targets[0].m_blendOp = RHI::BlendOp::SubtractReverse;
  228. for (size_t i = 0; i < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++i)
  229. {
  230. RHI::Ptr<RHI::ShaderResourceGroupLayout> srgLayout = CreateShaderResourceGroupLayout(i);
  231. AZ::RHI::ShaderResourceGroupBindingInfo bindingInfo = CreateShaderResouceGroupBindingInfo(i);
  232. m_pipelineLayoutDescriptor->AddShaderResourceGroupLayoutInfo(*srgLayout.get(), bindingInfo);
  233. m_srgLayouts.push_back(srgLayout);
  234. }
  235. m_pipelineLayoutDescriptor->Finalize();
  236. }
  237. void TearDown() override
  238. {
  239. m_name = Name{};
  240. m_drawListName = Name{};
  241. for (size_t i = 0; i < m_bindings.size(); ++i)
  242. {
  243. m_bindings[i] = {};
  244. m_bindingsFullSpecialization[i] = {};
  245. m_bindingsPartialSpecialization[i] = {};
  246. }
  247. m_srgLayouts.clear();
  248. m_pipelineLayoutDescriptor = nullptr;
  249. m_shaderOptionGroupLayoutForAsset = nullptr;
  250. m_shaderOptionGroupLayoutForAssetPartialSpecialization = nullptr;
  251. m_shaderOptionGroupLayoutForAssetFullSpecialization = nullptr;
  252. m_shaderOptionGroupLayoutForVariants = nullptr;
  253. RPITestFixture::TearDown();
  254. }
  255. const AZ::RPI::ShaderOptionDescriptor& GetShaderOptionDescriptor(SpecializationType specializationType, uint32_t index)
  256. {
  257. switch (specializationType)
  258. {
  259. case SpecializationType::Partial:
  260. return m_bindingsPartialSpecialization[index];
  261. case SpecializationType::Full:
  262. return m_bindingsFullSpecialization[index];
  263. case SpecializationType::None:
  264. default:
  265. return m_bindings[index];
  266. }
  267. }
  268. AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> CreateShaderOptionLayout(
  269. AZ::RHI::Handle<size_t> indexToOmit = {}, SpecializationType specializationType = SpecializationType::None)
  270. {
  271. using namespace AZ;
  272. RPI::Ptr<RPI::ShaderOptionGroupLayout> layout = RPI::ShaderOptionGroupLayout::Create();
  273. for (uint32_t i = 0; i < m_bindings.size(); ++i)
  274. {
  275. // Allows omitting a single option to test for missing options.
  276. if (indexToOmit.GetIndex() != i)
  277. {
  278. layout->AddShaderOption(GetShaderOptionDescriptor(specializationType, i));
  279. }
  280. }
  281. layout->Finalize();
  282. return layout;
  283. }
  284. AZ::Name CreateShaderResourceGroupId(size_t index)
  285. {
  286. using namespace AZ;
  287. return Name{ AZStd::to_string(index) };
  288. }
  289. RHI::Ptr<RHI::ShaderResourceGroupLayout> CreateShaderResourceGroupLayout(size_t index)
  290. {
  291. using namespace AZ;
  292. Name srgId = CreateShaderResourceGroupId(index);
  293. // Creates a simple SRG asset with a unique SRG layout hash (based on the index).
  294. RHI::Ptr<RHI::ShaderResourceGroupLayout> srgLayout = RHI::ShaderResourceGroupLayout::Create();
  295. srgLayout->SetName(srgId);
  296. srgLayout->SetBindingSlot(aznumeric_caster(index));
  297. srgLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
  298. srgId, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Raw, 1, 4, static_cast<uint32_t>(index), static_cast<uint32_t>(index)});
  299. EXPECT_TRUE(srgLayout->Finalize());
  300. return srgLayout;
  301. }
  302. AZ::RHI::ShaderResourceGroupBindingInfo CreateShaderResouceGroupBindingInfo(size_t index)
  303. {
  304. Name srgId = CreateShaderResourceGroupId(index);
  305. AZ::RHI::ShaderResourceGroupBindingInfo bindingInfo;
  306. bindingInfo.m_resourcesRegisterMap.insert({ srgId, RHI::ResourceBindingInfo{RHI::ShaderStageMask::Vertex, static_cast<uint32_t>(index), static_cast<uint32_t>(index)} });
  307. return bindingInfo;
  308. }
  309. AZ::RPI::ShaderInputContract CreateSimpleShaderInputContract()
  310. {
  311. AZ::RPI::ShaderInputContract contract;
  312. AZ::RPI::ShaderInputContract::StreamChannelInfo channel;
  313. channel.m_semantic = AZ::RHI::ShaderSemantic{ AZ::Name{"POSITION"} };
  314. contract.m_streamChannels.push_back(channel);
  315. return contract;
  316. }
  317. AZ::RPI::ShaderOutputContract CreateSimpleShaderOutputContract()
  318. {
  319. AZ::RPI::ShaderOutputContract contract;
  320. AZ::RPI::ShaderOutputContract::ColorAttachmentInfo attachment;
  321. attachment.m_componentCount = 4;
  322. contract.m_requiredColorAttachments.push_back(attachment);
  323. return contract;
  324. }
  325. RPI::ShaderVariantListSourceData::VariantInfo CreateVariantInfo(uint32_t stableId, AZStd::vector<AZStd::string> optionValues)
  326. {
  327. RPI::ShaderVariantListSourceData::VariantInfo variantInfo;
  328. variantInfo.m_stableId = stableId;
  329. auto nextValue = optionValues.begin();
  330. auto nextOption = m_shaderOptionGroupLayoutForVariants->GetShaderOptions().begin();
  331. while (nextValue != optionValues.end() &&
  332. nextOption != m_shaderOptionGroupLayoutForVariants->GetShaderOptions().end())
  333. {
  334. if (nextValue->empty())
  335. {
  336. // TODO (To consider) If we decide to support gaps (unqualified options) in the lookup key
  337. // we can actually remove this check
  338. variantInfo.m_options[nextOption->GetName()] = nextOption->GetDefaultValue();
  339. }
  340. else
  341. {
  342. variantInfo.m_options[nextOption->GetName()] = Name{*nextValue};
  343. }
  344. nextValue++;
  345. nextOption++;
  346. }
  347. return variantInfo;
  348. }
  349. // Creates and returns a shader option group with the specified option values.
  350. RPI::ShaderOptionGroup CreateShaderOptionGroup(AZStd::vector<Name> optionValues)
  351. {
  352. RPI::ShaderOptionGroup shaderOptionGroup(m_shaderOptionGroupLayoutForVariants);
  353. auto nextValue = optionValues.begin();
  354. auto nextOption = m_shaderOptionGroupLayoutForVariants->GetShaderOptions().begin();
  355. while (nextValue != optionValues.end() &&
  356. nextOption != m_shaderOptionGroupLayoutForVariants->GetShaderOptions().end())
  357. {
  358. if (nextValue->IsEmpty())
  359. {
  360. // TODO (To consider) If we decide to support gaps (unqualified options) in the lookup key
  361. // we can actually remove this check
  362. shaderOptionGroup.SetValue(nextOption->GetName(), nextOption->GetDefaultValue());
  363. }
  364. else
  365. {
  366. shaderOptionGroup.SetValue(nextOption->GetName(), *nextValue);
  367. }
  368. nextValue++;
  369. nextOption++;
  370. }
  371. return shaderOptionGroup;
  372. }
  373. Data::Asset<RPI::ShaderVariantAsset> CreateTestShaderVariantAsset(RPI::ShaderVariantId id, RPI::ShaderVariantStableId stableId,
  374. bool isFullyBaked,
  375. const AZStd::vector<RHI::ShaderStage>& stagesToActivate = {RHI::ShaderStage::Vertex, RHI::ShaderStage::Fragment})
  376. {
  377. RPI::ShaderVariantAssetCreator shaderVariantAssetCreator;
  378. shaderVariantAssetCreator.Begin(Uuid::CreateRandom(), id, stableId, isFullyBaked);
  379. for (RHI::ShaderStage rhiStage : stagesToActivate)
  380. {
  381. RHI::Ptr<RHI::ShaderStageFunction> vertexStageFunction = aznew TestShaderStageFunction(rhiStage);
  382. shaderVariantAssetCreator.SetShaderFunction(rhiStage, vertexStageFunction);
  383. }
  384. Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset;
  385. shaderVariantAssetCreator.End(shaderVariantAsset);
  386. return shaderVariantAsset;
  387. }
  388. AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> GetShaderOptionGroupForAssets(SpecializationType specializationType)
  389. {
  390. switch (specializationType)
  391. {
  392. case SpecializationType::None:
  393. return m_shaderOptionGroupLayoutForAsset;
  394. case SpecializationType::Partial:
  395. return m_shaderOptionGroupLayoutForAssetPartialSpecialization;
  396. case SpecializationType::Full:
  397. return m_shaderOptionGroupLayoutForAssetFullSpecialization;
  398. default:
  399. return nullptr;
  400. }
  401. }
  402. void BeginCreatingTestShaderAsset(AZ::RPI::ShaderAssetCreator& creator,
  403. const AZStd::vector<RHI::ShaderStage>& stagesToActivate = {RHI::ShaderStage::Vertex, RHI::ShaderStage::Fragment},
  404. SpecializationType specializationType = SpecializationType::None)
  405. {
  406. using namespace AZ;
  407. creator.Begin(Uuid::CreateRandom());
  408. creator.SetName(m_name);
  409. creator.SetDrawListName(m_drawListName);
  410. creator.SetShaderOptionGroupLayout(GetShaderOptionGroupForAssets(specializationType));
  411. creator.BeginAPI(RHI::Factory::Get().GetType());
  412. creator.BeginSupervariant(AZ::Name{}); // The default (first) supervariant MUST be nameless.
  413. creator.SetSrgLayoutList(m_srgLayouts);
  414. creator.SetPipelineLayout(m_pipelineLayoutDescriptor);
  415. creator.SetRenderStates(m_renderStates);
  416. creator.SetInputContract(CreateSimpleShaderInputContract());
  417. creator.SetOutputContract(CreateSimpleShaderOutputContract());
  418. creator.SetUseSpecializationConstants(specializationType != SpecializationType::None);
  419. RHI::ShaderStageAttributeMapList attributeMaps;
  420. attributeMaps.resize(RHI::ShaderStageCount);
  421. creator.SetShaderStageAttributeMapList(attributeMaps);
  422. Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset = CreateTestShaderVariantAsset(RPI::ShaderVariantId{}, RPI::ShaderVariantStableId{0}, false, stagesToActivate);
  423. creator.SetRootShaderVariantAsset(shaderVariantAsset);
  424. creator.EndSupervariant();
  425. }
  426. //! Used to finish creating a shader that began with BeginCreatingTestShaderAsset(). Call this after adding all the desired shader variants.
  427. AZ::Data::Asset<AZ::RPI::ShaderAsset> EndCreatingTestShaderAsset(RPI::ShaderAssetCreator& creator)
  428. {
  429. Data::Asset<RPI::ShaderAsset> shaderAsset;
  430. if (creator.EndAPI())
  431. {
  432. creator.End(shaderAsset);
  433. }
  434. return shaderAsset;
  435. }
  436. AZ::Data::Asset<AZ::RPI::ShaderAsset> CreateShaderAsset()
  437. {
  438. using namespace AZ;
  439. RPI::ShaderAssetCreator creator;
  440. BeginCreatingTestShaderAsset(creator);
  441. Data::Asset<RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  442. return shaderAsset;
  443. }
  444. //! The tree will only contain the root variant.
  445. AZ::Data::Asset<AZ::RPI::ShaderVariantTreeAsset> CreateEmptyShaderVariantTreeAsset(Data::Asset<RPI::ShaderAsset> shaderAsset)
  446. {
  447. using namespace AZ;
  448. AZStd::vector<RPI::ShaderVariantListSourceData::VariantInfo> shaderVariantList;
  449. RPI::ShaderVariantTreeAssetCreator creator;
  450. creator.Begin(Uuid::CreateRandom());
  451. creator.SetShaderOptionGroupLayout(*shaderAsset->GetShaderOptionGroupLayout());
  452. creator.SetVariantInfos(shaderVariantList);
  453. Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset;
  454. if (!creator.End(shaderVariantTreeAsset))
  455. {
  456. return {};
  457. }
  458. return shaderVariantTreeAsset;
  459. }
  460. AZ::Data::Asset<AZ::RPI::ShaderVariantTreeAsset> CreateShaderVariantTreeAssetForSearch(Data::Asset<RPI::ShaderAsset> shaderAsset)
  461. {
  462. using namespace AZ;
  463. AZStd::vector<RPI::ShaderVariantListSourceData::VariantInfo> shaderVariantList;
  464. shaderVariantList.push_back(CreateVariantInfo(1, { AZStd::string{"Fuchsia"} }));
  465. shaderVariantList.push_back(CreateVariantInfo(2, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"} }));
  466. shaderVariantList.push_back(CreateVariantInfo(3, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"}, AZStd::string{"50"} }));
  467. shaderVariantList.push_back(CreateVariantInfo(4, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"}, AZStd::string{"50"}, AZStd::string{"Off"} }));
  468. shaderVariantList.push_back(CreateVariantInfo(5, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"}, AZStd::string{"50"}, AZStd::string{"On"} }));
  469. shaderVariantList.push_back(CreateVariantInfo(6, { AZStd::string{"Teal"} }));
  470. shaderVariantList.push_back(CreateVariantInfo(7, { AZStd::string{"Teal"}, AZStd::string{"Quality::Sublime"} }));
  471. RPI::ShaderVariantTreeAssetCreator creator;
  472. creator.Begin(Uuid::CreateRandom()) ;
  473. creator.SetShaderOptionGroupLayout(*shaderAsset->GetShaderOptionGroupLayout());
  474. creator.SetVariantInfos(shaderVariantList);
  475. Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset;
  476. if (!creator.End(shaderVariantTreeAsset))
  477. {
  478. return {};
  479. }
  480. return shaderVariantTreeAsset;
  481. }
  482. void ValidateShaderAsset(const AZ::Data::Asset<AZ::RPI::ShaderAsset>& shaderAsset)
  483. {
  484. using namespace AZ;
  485. EXPECT_TRUE(shaderAsset);
  486. EXPECT_EQ(shaderAsset->GetName(), m_name);
  487. EXPECT_EQ(shaderAsset->GetDrawListName(), m_drawListName);
  488. EXPECT_EQ(shaderAsset->GetShaderOptionGroupLayout()->GetHash(), m_shaderOptionGroupLayoutForAsset->GetHash());
  489. EXPECT_EQ(shaderAsset->GetPipelineLayoutDescriptor()->GetHash(), m_pipelineLayoutDescriptor->GetHash());
  490. for (size_t i = 0; i < shaderAsset->GetShaderResourceGroupLayouts().size(); ++i)
  491. {
  492. auto srgLayouts = shaderAsset->GetShaderResourceGroupLayouts();
  493. auto& srgLayout = srgLayouts[i];
  494. EXPECT_EQ(srgLayout->GetHash(), m_srgLayouts[i]->GetHash());
  495. EXPECT_EQ(shaderAsset->FindShaderResourceGroupLayout(CreateShaderResourceGroupId(i))->GetHash(), srgLayout->GetHash());
  496. }
  497. }
  498. void ValidateShader(const AZ::Data::Instance<AZ::RPI::Shader>& shader)
  499. {
  500. using namespace AZ;
  501. EXPECT_TRUE(shader);
  502. EXPECT_TRUE(shader->GetAsset());
  503. auto shaderAsset = shader->GetAsset();
  504. EXPECT_EQ(shader->GetPipelineStateType(), shaderAsset->GetPipelineStateType());
  505. using ShaderResourceGroupLayoutSpan = AZStd::span<const AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout>>;
  506. ShaderResourceGroupLayoutSpan shaderResourceGroupLayoutSpan = shader->GetShaderResourceGroupLayouts();
  507. ShaderResourceGroupLayoutSpan shaderAssetResourceGroupLayoutSpan = shader->GetShaderResourceGroupLayouts();
  508. EXPECT_EQ(shaderResourceGroupLayoutSpan.data(), shaderAssetResourceGroupLayoutSpan.data());
  509. EXPECT_EQ(shaderResourceGroupLayoutSpan.size(), shaderAssetResourceGroupLayoutSpan.size());
  510. const RPI::ShaderVariant& rootShaderVariant = shader->GetVariant( RPI::ShaderVariantStableId{0} );
  511. RHI::PipelineStateDescriptorForDraw descriptorForDraw;
  512. rootShaderVariant.ConfigurePipelineState(descriptorForDraw);
  513. EXPECT_EQ(descriptorForDraw.m_pipelineLayoutDescriptor->GetHash(), m_pipelineLayoutDescriptor->GetHash());
  514. EXPECT_NE(descriptorForDraw.m_vertexFunction, nullptr);
  515. EXPECT_NE(descriptorForDraw.m_fragmentFunction, nullptr);
  516. EXPECT_EQ(descriptorForDraw.m_renderStates.GetHash(), m_renderStates.GetHash());
  517. EXPECT_EQ(descriptorForDraw.m_inputStreamLayout.GetHash(), HashValue64{ 0 }); // ConfigurePipelineState shouldn't touch descriptorForDraw.m_inputStreamLayout
  518. EXPECT_EQ(descriptorForDraw.m_renderAttachmentConfiguration.GetHash(), RHI::RenderAttachmentConfiguration().GetHash()); // ConfigurePipelineState shouldn't touch descriptorForDraw.m_outputAttachmentLayout
  519. // Actual layout content doesn't matter for this test, it just needs to be set up to pass validation inside AcquirePipelineState().
  520. descriptorForDraw.m_inputStreamLayout.SetTopology(RHI::PrimitiveTopology::TriangleList);
  521. descriptorForDraw.m_inputStreamLayout.Finalize();
  522. RHI::RenderAttachmentLayoutBuilder builder;
  523. builder.AddSubpass()
  524. ->RenderTargetAttachment(RHI::Format::R8G8B8A8_SNORM)
  525. ->DepthStencilAttachment(RHI::Format::R32_FLOAT);
  526. builder.End(descriptorForDraw.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  527. const RHI::PipelineState* pipelineState = shader->AcquirePipelineState(descriptorForDraw);
  528. EXPECT_NE(pipelineState, nullptr);
  529. }
  530. AZStd::array<AZ::RPI::ShaderOptionDescriptor, 4> m_bindings;
  531. AZStd::array<AZ::RPI::ShaderOptionDescriptor, 4> m_bindingsFullSpecialization;
  532. AZStd::array<AZ::RPI::ShaderOptionDescriptor, 4> m_bindingsPartialSpecialization;
  533. AZ::Name m_name;
  534. AZ::Name m_drawListName;
  535. AZ::RHI::Ptr<AZ::RHI::PipelineLayoutDescriptor> m_pipelineLayoutDescriptor;
  536. AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> m_shaderOptionGroupLayoutForAsset;
  537. AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> m_shaderOptionGroupLayoutForAssetPartialSpecialization;
  538. AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> m_shaderOptionGroupLayoutForAssetFullSpecialization;
  539. AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> m_shaderOptionGroupLayoutForVariants;
  540. AZ::RHI::RenderStates m_renderStates;
  541. AZStd::fixed_vector<AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout>, AZ::RHI::Limits::Pipeline::ShaderResourceGroupCountMax> m_srgLayouts;
  542. };
  543. TEST_F(ShaderTests, ShaderOptionBindingTest)
  544. {
  545. using namespace AZ;
  546. EXPECT_EQ(m_bindings[0].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(4, 0) });
  547. EXPECT_EQ(m_bindings[1].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(3, 4) });
  548. EXPECT_EQ(m_bindings[2].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(8, 7) });
  549. EXPECT_EQ(m_bindings[3].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(1, 15) });
  550. EXPECT_TRUE(m_bindings[0].FindValue(Name("Navy")).IsValid());
  551. EXPECT_FALSE(m_bindings[0].FindValue(Name("Color::Navy")).IsValid()); // Not found - Color is unscoped
  552. EXPECT_TRUE(m_bindings[1].FindValue(Name("Quality::Average")).IsValid());
  553. EXPECT_FALSE(m_bindings[1].FindValue(Name("Average")).IsValid()); // Not found - Quality is scoped
  554. EXPECT_FALSE(m_bindings[1].FindValue(Name("Cake")).IsValid()); // Not found - Cake is not on the list
  555. EXPECT_FALSE(m_bindings[1].FindValue(Name("Quality::Cake")).IsValid()); // Not found - still not on the list
  556. EXPECT_TRUE(m_bindings[2].FindValue(Name("5")).IsValid());
  557. EXPECT_TRUE(m_bindings[2].FindValue(Name("200")).IsValid());
  558. EXPECT_TRUE(m_bindings[2].FindValue(Name("42")).IsValid());
  559. EXPECT_FALSE(m_bindings[2].FindValue(Name("-1")).IsValid()); // Not found - less than MinValue
  560. EXPECT_FALSE(m_bindings[2].FindValue(Name("1001")).IsValid()); // Not found - more than MaxValue
  561. EXPECT_TRUE(m_bindings[3].FindValue(Name("Off")).IsValid());
  562. EXPECT_TRUE(m_bindings[3].FindValue(Name("On")).IsValid());
  563. EXPECT_FALSE(m_bindings[3].FindValue(Name("False")).IsValid()); // Not found - the correct user-defined id is Off
  564. EXPECT_FALSE(m_bindings[3].FindValue(Name("True")).IsValid()); // Not found - the correct user-defined id is On
  565. EXPECT_EQ(m_bindings[0].GetValueName(RPI::ShaderOptionValue(4)), Name("Navy"));
  566. EXPECT_EQ(m_bindings[1].GetValueName(RPI::ShaderOptionValue(3)), Name("Quality::Average"));
  567. EXPECT_EQ(m_bindings[2].GetValueName(RPI::ShaderOptionValue(200)), Name("200"));
  568. EXPECT_EQ(m_bindings[3].GetValueName(RPI::ShaderOptionValue(0)), Name("Off"));
  569. EXPECT_EQ(m_bindings[3].GetValueName(RPI::ShaderOptionValue(1)), Name("On"));
  570. EXPECT_TRUE(m_bindings[2].GetValueName(RPI::ShaderOptionValue(-1)).IsEmpty()); // No matching value
  571. EXPECT_TRUE(m_bindings[2].GetValueName(RPI::ShaderOptionValue(1001)).IsEmpty()); // No matching value
  572. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  573. bool success = shaderOptionGroupLayout->AddShaderOption(m_bindings[0]);
  574. EXPECT_TRUE(success);
  575. success = shaderOptionGroupLayout->AddShaderOption(m_bindings[1]);
  576. EXPECT_TRUE(success);
  577. success = shaderOptionGroupLayout->AddShaderOption(m_bindings[2]);
  578. EXPECT_TRUE(success);
  579. success = shaderOptionGroupLayout->AddShaderOption(m_bindings[3]);
  580. EXPECT_TRUE(success);
  581. shaderOptionGroupLayout->Finalize();
  582. EXPECT_TRUE(shaderOptionGroupLayout->IsFinalized());
  583. RPI::ShaderOptionGroup testGroup(shaderOptionGroupLayout);
  584. m_bindings[0].Set(testGroup, m_bindings[0].FindValue(Name("Gray")));
  585. EXPECT_EQ(m_bindings[0].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(8).GetIndex());
  586. m_bindings[0].Set(testGroup, RPI::ShaderOptionValue(1));
  587. EXPECT_EQ(m_bindings[0].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(1).GetIndex());
  588. testGroup.SetValue(Name("Color"), Name("Olive"));
  589. EXPECT_EQ(testGroup.GetValue(Name("Color")).GetIndex(), RPI::ShaderOptionValue(3).GetIndex());
  590. testGroup.SetValue(Name("Color"), RPI::ShaderOptionValue(5));
  591. EXPECT_EQ(testGroup.GetValue(Name("Color")).GetIndex(), RPI::ShaderOptionValue(5).GetIndex());
  592. testGroup.SetValue(RPI::ShaderOptionIndex(0), Name("Lime"));
  593. EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(0)).GetIndex(), RPI::ShaderOptionValue(10).GetIndex());
  594. testGroup.SetValue(RPI::ShaderOptionIndex(0), RPI::ShaderOptionValue(0));
  595. EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(0)).GetIndex(), RPI::ShaderOptionValue(0).GetIndex());
  596. m_bindings[1].Set(testGroup, m_bindings[1].FindValue(Name("Quality::Average")));
  597. EXPECT_EQ(m_bindings[1].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(3).GetIndex());
  598. m_bindings[1].Set(testGroup, RPI::ShaderOptionValue(1));
  599. EXPECT_EQ(m_bindings[1].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(1).GetIndex());
  600. testGroup.SetValue(Name("Quality"), Name("Quality::Ultra"));
  601. EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(6).GetIndex());
  602. testGroup.SetValue(Name("Quality"), RPI::ShaderOptionValue(5));
  603. EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(5).GetIndex());
  604. testGroup.SetValue(RPI::ShaderOptionIndex(1), Name("Quality::Auto"));
  605. EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(1)).GetIndex(), RPI::ShaderOptionValue(0).GetIndex());
  606. testGroup.SetValue(RPI::ShaderOptionIndex(1), RPI::ShaderOptionValue(2));
  607. EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(1)).GetIndex(), RPI::ShaderOptionValue(2).GetIndex());
  608. m_bindings[2].Set(testGroup, m_bindings[2].FindValue(Name("150")));
  609. EXPECT_EQ(m_bindings[2].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(150).GetIndex());
  610. m_bindings[2].Set(testGroup, RPI::ShaderOptionValue(120));
  611. EXPECT_EQ(m_bindings[2].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(120).GetIndex());
  612. testGroup.SetValue(Name("NumberSamples"), Name("101"));
  613. EXPECT_EQ(testGroup.GetValue(Name("NumberSamples")).GetIndex(), RPI::ShaderOptionValue(101).GetIndex());
  614. testGroup.SetValue(Name("NumberSamples"), RPI::ShaderOptionValue(102));
  615. EXPECT_EQ(testGroup.GetValue(Name("NumberSamples")).GetIndex(), RPI::ShaderOptionValue(102).GetIndex());
  616. testGroup.SetValue(RPI::ShaderOptionIndex(2), Name("103"));
  617. EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(2)).GetIndex(), RPI::ShaderOptionValue(103).GetIndex());
  618. testGroup.SetValue(RPI::ShaderOptionIndex(2), RPI::ShaderOptionValue(104));
  619. EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(2)).GetIndex(), RPI::ShaderOptionValue(104).GetIndex());
  620. // Tests for invalid or Null value id
  621. // Setting a valid value id changes the key
  622. testGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
  623. EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(7).GetIndex());
  624. // "Cake" is delicious, but it's not a valid option for "Quality"
  625. // Setting an invalid value id does nothing - it's ignored, so the key remains the same
  626. AZ_TEST_START_TRACE_SUPPRESSION;
  627. testGroup.SetValue(Name("Quality"), Name("Cake"));
  628. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  629. EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(7).GetIndex());
  630. // ClearValue clears the mask
  631. testGroup.ClearValue(Name("Quality"));
  632. EXPECT_EQ(testGroup.GetValue(Name("Quality")).IsNull(), true);
  633. }
  634. TEST_F(ShaderTests, ShaderOptionGroupLayoutTest)
  635. {
  636. using namespace AZ;
  637. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  638. bool success = shaderOptionGroupLayout->AddShaderOption(m_bindings[0]);
  639. EXPECT_TRUE(success);
  640. success = shaderOptionGroupLayout->AddShaderOption(m_bindings[1]);
  641. EXPECT_TRUE(success);
  642. success = shaderOptionGroupLayout->AddShaderOption(m_bindings[2]);
  643. EXPECT_TRUE(success);
  644. success = shaderOptionGroupLayout->AddShaderOption(m_bindings[3]);
  645. EXPECT_TRUE(success);
  646. auto intRangeType = RPI::ShaderOptionType::IntegerRange;
  647. uint32_t order = m_bindings[3].GetOrder() + 1; // The tests below will fail anyway, but still
  648. ErrorMessageFinder errorMessageFinder;
  649. // Overlaps previous mask.
  650. errorMessageFinder.Reset();
  651. errorMessageFinder.AddExpectedErrorMessage("mask overlaps with previously added masks");
  652. AZStd::vector<RPI::ShaderOptionValuePair> list0;
  653. list0.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
  654. list0.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
  655. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 6, order++, list0, Name("0") });
  656. EXPECT_FALSE(success);
  657. errorMessageFinder.CheckExpectedErrorsFound();
  658. // Add shader option that extends past end of bit mask.
  659. errorMessageFinder.Reset();
  660. errorMessageFinder.AddExpectedErrorMessage("exceeds size of mask");
  661. AZStd::vector<RPI::ShaderOptionValuePair> list1;
  662. list1.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
  663. list1.push_back({ Name("255"), RPI::ShaderOptionValue(255) }); // 8+ bit
  664. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, RPI::ShaderVariantKeyBitCount - 4, order++, list1, Name("0") });
  665. EXPECT_FALSE(success);
  666. errorMessageFinder.CheckExpectedErrorsFound();
  667. // Add shader option with empty name.
  668. errorMessageFinder.Reset();
  669. errorMessageFinder.AddExpectedErrorMessage("empty name");
  670. AZStd::vector<RPI::ShaderOptionValuePair> list2;
  671. list2.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
  672. list2.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
  673. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{}, intRangeType, 16, order++, list2, Name("0") });
  674. EXPECT_FALSE(success);
  675. errorMessageFinder.CheckExpectedErrorsFound();
  676. // Add shader option with empty bits.
  677. errorMessageFinder.Reset();
  678. errorMessageFinder.AddExpectedErrorMessage("has zero bits");
  679. AZStd::vector<RPI::ShaderOptionValuePair> list3;
  680. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 16, order++, list3, Name("0") });
  681. EXPECT_FALSE(success);
  682. errorMessageFinder.CheckExpectedErrorsFound();
  683. // An integer range option must have at least two values defining the range
  684. errorMessageFinder.Reset();
  685. errorMessageFinder.AddExpectedErrorMessage("has zero bits");
  686. AZStd::vector<RPI::ShaderOptionValuePair> list3b;
  687. list3b.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
  688. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 16, order++, list3b, Name("0") });
  689. EXPECT_FALSE(success);
  690. errorMessageFinder.CheckExpectedErrorsFound();
  691. // Add a shader option with an order that collides with an existing shader option
  692. errorMessageFinder.Reset();
  693. errorMessageFinder.AddExpectedErrorMessage("has the same order");
  694. uint32_t bitOffset = m_bindings[3].GetBitOffset() + m_bindings[3].GetBitCount();
  695. AZStd::vector<RPI::ShaderOptionValuePair> list4;
  696. list4.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
  697. list4.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
  698. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, bitOffset, 0, list4, Name("0") });
  699. EXPECT_FALSE(success);
  700. errorMessageFinder.CheckExpectedErrorsFound();
  701. // Add shader option with an invalid default int value.
  702. errorMessageFinder.Reset();
  703. errorMessageFinder.AddExpectedErrorMessage("invalid default value");
  704. AZStd::vector<RPI::ShaderOptionValuePair> list6;
  705. list6.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
  706. list6.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
  707. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 16, order++, list6, Name("3") });
  708. EXPECT_FALSE(success);
  709. errorMessageFinder.CheckExpectedErrorsFound();
  710. // Add shader option with an invalid default enum value.
  711. errorMessageFinder.Reset();
  712. errorMessageFinder.AddExpectedErrorMessage("invalid default value");
  713. AZStd::vector<RPI::ShaderOptionValuePair> list7;
  714. list7.push_back({ Name("TypeA"), RPI::ShaderOptionValue(0) });
  715. list7.push_back({ Name("TypeB"), RPI::ShaderOptionValue(1) });
  716. list7.push_back({ Name("TypeC"), RPI::ShaderOptionValue(2) });
  717. success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, RPI::ShaderOptionType::Enumeration, 16, order++, list7, Name("TypeO") });
  718. EXPECT_FALSE(success);
  719. errorMessageFinder.CheckExpectedErrorsFound();
  720. // Test access before finalize.
  721. EXPECT_FALSE(shaderOptionGroupLayout->IsFinalized());
  722. errorMessageFinder.Reset();
  723. errorMessageFinder.AddExpectedErrorMessage("ShaderOptionGroupLayout is not finalized", 4);
  724. EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[0].GetName()), RPI::ShaderOptionIndex());
  725. EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[1].GetName()), RPI::ShaderOptionIndex());
  726. EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[2].GetName()), RPI::ShaderOptionIndex());
  727. EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[3].GetName()), RPI::ShaderOptionIndex());
  728. errorMessageFinder.CheckExpectedErrorsFound();
  729. {
  730. errorMessageFinder.Reset();
  731. errorMessageFinder.AddExpectedErrorMessage("ShaderOptionGroupLayout is not finalized");
  732. RPI::ShaderVariantKey testKey = 1;
  733. EXPECT_FALSE(shaderOptionGroupLayout->IsValidShaderVariantKey(testKey));
  734. errorMessageFinder.CheckExpectedErrorsFound();
  735. }
  736. errorMessageFinder.Disable();
  737. shaderOptionGroupLayout->Finalize();
  738. EXPECT_TRUE(shaderOptionGroupLayout->IsFinalized());
  739. EXPECT_EQ(shaderOptionGroupLayout->GetShaderOptionCount(), 4);
  740. EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(0)), m_bindings[0]);
  741. EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(1)), m_bindings[1]);
  742. EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(2)), m_bindings[2]);
  743. EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(3)), m_bindings[3]);
  744. RPI::ShaderVariantKey unionMask;
  745. for (const auto& binding : m_bindings)
  746. {
  747. unionMask |= binding.GetBitMask();
  748. }
  749. EXPECT_EQ(unionMask, shaderOptionGroupLayout->GetBitMask());
  750. EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[0].GetBitMask()));
  751. EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[1].GetBitMask()));
  752. EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[2].GetBitMask()));
  753. EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[3].GetBitMask()));
  754. // Test value-lookup functions
  755. auto& colorOption = shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex{ 0 });
  756. EXPECT_EQ(colorOption.FindValue(Name{ "Navy" }).GetIndex(), 4);
  757. EXPECT_EQ(colorOption.FindValue(Name{ "Purple" }).GetIndex(), 5);
  758. EXPECT_FALSE(colorOption.FindValue(Name{ "Blah" }).IsValid());
  759. EXPECT_EQ(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 0 }, Name{ "Navy" }).GetIndex(), 4);
  760. EXPECT_EQ(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 0 }, Name{ "Purple" }).GetIndex(), 5);
  761. EXPECT_EQ(shaderOptionGroupLayout->FindValue(Name{ "Color" }, Name{ "Navy" }).GetIndex(), 4);
  762. EXPECT_EQ(shaderOptionGroupLayout->FindValue(Name{ "Color" }, Name{ "Purple" }).GetIndex(), 5);
  763. EXPECT_FALSE(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 0 }, Name{ "Blah" }).IsValid());
  764. EXPECT_FALSE(shaderOptionGroupLayout->FindValue(Name{ "Color" }, Name{ "Blah" }).IsValid());
  765. EXPECT_FALSE(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{}, Name{ "Navy" }).IsValid());
  766. EXPECT_FALSE(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 100 }, Name{ "Navy" }).IsValid());
  767. EXPECT_FALSE(shaderOptionGroupLayout->FindValue(Name{ "Blah" }, Name{ "Navy" }).IsValid());
  768. EXPECT_FALSE(shaderOptionGroupLayout->FindShaderOptionIndex(Name{ "Invalid" }).IsValid());
  769. }
  770. TEST_F(ShaderTests, ShaderOptionGroupLayoutSpecializationTest)
  771. {
  772. using namespace AZ;
  773. AZStd::vector<RPI::ShaderOptionValuePair> idList4;
  774. idList4.push_back({ Name("True"), RPI::ShaderOptionValue(0) });
  775. idList4.push_back({ Name("False"), RPI::ShaderOptionValue(1) });
  776. {
  777. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  778. bool success = shaderOptionGroupLayout->AddShaderOption(
  779. RPI::ShaderOptionDescriptor{ Name{ "Specialized1" }, RPI::ShaderOptionType::Boolean, 0, 0, idList4, Name("False"), 0, 0 });
  780. EXPECT_TRUE(success);
  781. success = shaderOptionGroupLayout->AddShaderOption(
  782. RPI::ShaderOptionDescriptor{ Name{ "Specialized2" }, RPI::ShaderOptionType::Boolean, 1, 1, idList4, Name("False"), 0, 1 });
  783. EXPECT_TRUE(success);
  784. success = shaderOptionGroupLayout->AddShaderOption(
  785. RPI::ShaderOptionDescriptor{ Name{ "Specialized3" }, RPI::ShaderOptionType::Boolean, 2, 2, idList4, Name("False"), 0, 2 });
  786. EXPECT_TRUE(success);
  787. shaderOptionGroupLayout->Finalize();
  788. EXPECT_TRUE(shaderOptionGroupLayout->IsFullySpecialized());
  789. EXPECT_TRUE(shaderOptionGroupLayout->UseSpecializationConstants());
  790. }
  791. {
  792. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  793. bool success = shaderOptionGroupLayout->AddShaderOption(
  794. RPI::ShaderOptionDescriptor{ Name{ "Specialized1" }, RPI::ShaderOptionType::Boolean, 0, 0, idList4, Name("False"), 0, 0 });
  795. EXPECT_TRUE(success);
  796. success = shaderOptionGroupLayout->AddShaderOption(
  797. RPI::ShaderOptionDescriptor{ Name{ "Specialized2" }, RPI::ShaderOptionType::Boolean, 1, 1, idList4, Name("False"), 0, -1 });
  798. EXPECT_TRUE(success);
  799. success = shaderOptionGroupLayout->AddShaderOption(
  800. RPI::ShaderOptionDescriptor{ Name{ "Specialized3" }, RPI::ShaderOptionType::Boolean, 2, 2, idList4, Name("False"), 0, 1 });
  801. EXPECT_TRUE(success);
  802. shaderOptionGroupLayout->Finalize();
  803. EXPECT_FALSE(shaderOptionGroupLayout->IsFullySpecialized());
  804. EXPECT_TRUE(shaderOptionGroupLayout->UseSpecializationConstants());
  805. }
  806. {
  807. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  808. bool success = shaderOptionGroupLayout->AddShaderOption(
  809. RPI::ShaderOptionDescriptor{ Name{ "Specialized1" }, RPI::ShaderOptionType::Boolean, 0, 0, idList4, Name("False"), 0, -1 });
  810. EXPECT_TRUE(success);
  811. success = shaderOptionGroupLayout->AddShaderOption(
  812. RPI::ShaderOptionDescriptor{ Name{ "Specialized2" }, RPI::ShaderOptionType::Boolean, 1, 1, idList4, Name("False"), 0, -1 });
  813. EXPECT_TRUE(success);
  814. success = shaderOptionGroupLayout->AddShaderOption(
  815. RPI::ShaderOptionDescriptor{ Name{ "Specialized3" }, RPI::ShaderOptionType::Boolean, 2, 2, idList4, Name("False"), 0, -1 });
  816. EXPECT_TRUE(success);
  817. shaderOptionGroupLayout->Finalize();
  818. EXPECT_FALSE(shaderOptionGroupLayout->IsFullySpecialized());
  819. EXPECT_FALSE(shaderOptionGroupLayout->UseSpecializationConstants());
  820. }
  821. }
  822. TEST_F(ShaderTests, ImplicitDefaultValue)
  823. {
  824. // Add shader option with no default value.
  825. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  826. AZStd::vector<RPI::ShaderOptionValuePair> values = AZ::RPI::CreateEnumShaderOptionValues({"A", "B", "C"});
  827. bool success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"NoDefaultSpecified"}, RPI::ShaderOptionType::Enumeration, 0, 0, values });
  828. EXPECT_TRUE(success);
  829. EXPECT_STREQ("A", shaderOptionGroupLayout->GetShaderOptions().back().GetDefaultValue().GetCStr());
  830. }
  831. TEST_F(ShaderTests, ShaderOptionGroupTest)
  832. {
  833. using namespace AZ;
  834. RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
  835. EXPECT_TRUE(group.GetShaderVariantId().IsEmpty());
  836. group.SetValue(RPI::ShaderOptionIndex(0), RPI::ShaderOptionValue(7));
  837. group.SetValue(RPI::ShaderOptionIndex(1), RPI::ShaderOptionValue(6));
  838. group.SetValue(RPI::ShaderOptionIndex(2), RPI::ShaderOptionValue(5));
  839. group.SetValue(RPI::ShaderOptionIndex(3), RPI::ShaderOptionValue(1));
  840. group.SetValue(group.FindShaderOptionIndex(m_bindings[0].GetName()), RPI::ShaderOptionValue(7));
  841. group.SetValue(group.FindShaderOptionIndex(m_bindings[1].GetName()), RPI::ShaderOptionValue(6));
  842. group.SetValue(group.FindShaderOptionIndex(m_bindings[2].GetName()), RPI::ShaderOptionValue(5));
  843. group.SetValue(group.FindShaderOptionIndex(m_bindings[3].GetName()), RPI::ShaderOptionValue(1));
  844. EXPECT_FALSE(group.GetShaderVariantId().IsEmpty());
  845. EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[0].GetName())).GetIndex(), 7);
  846. EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[1].GetName())).GetIndex(), 6);
  847. EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[2].GetName())).GetIndex(), 5);
  848. EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[3].GetName())).GetIndex(), 1);
  849. EXPECT_EQ(group.FindShaderOptionIndex(Name{}), RPI::ShaderOptionIndex{});
  850. EXPECT_EQ(group.FindShaderOptionIndex(Name{ "Invalid" }), RPI::ShaderOptionIndex{});
  851. // Helper methods - these are suboptimal since because they fetch index from id
  852. // The intended use for these methods is in prototypes and simple sample code
  853. group.SetValue(Name("Color"), Name("Fuchsia")); // 13
  854. group.SetValue(Name("Quality"), Name("Quality::Sublime")); // 7
  855. group.SetValue(Name("NumberSamples"), Name("190")); // 190
  856. group.SetValue(Name("Raytracing"), Name("On")); // 1
  857. EXPECT_EQ(group.GetValue(Name("Color")).GetIndex(), 13);
  858. EXPECT_EQ(group.GetValue(Name("Quality")).GetIndex(), 7);
  859. EXPECT_EQ(group.GetValue(Name("NumberSamples")).GetIndex(), 190);
  860. EXPECT_EQ(group.GetValue(Name("Raytracing")).GetIndex(), 1);
  861. }
  862. RPI::Ptr<RPI::ShaderOptionGroupLayout> CreateOptionsLayoutWithAllBools()
  863. {
  864. AZStd::vector<RPI::ShaderOptionValuePair> boolIdList;
  865. boolIdList.push_back({Name("Off"), RPI::ShaderOptionValue(0)});
  866. boolIdList.push_back({Name("On"), RPI::ShaderOptionValue(1)});
  867. RPI::Ptr<RPI::ShaderOptionGroupLayout> layout = RPI::ShaderOptionGroupLayout::Create();
  868. for (uint32_t i = 0; i < AZ::RPI::ShaderVariantKeyBitCount; ++i)
  869. {
  870. RPI::ShaderOptionDescriptor option{
  871. Name{AZStd::string::format("option%d", i)},
  872. RPI::ShaderOptionType::Boolean,
  873. i,
  874. i,
  875. boolIdList,
  876. Name{"Off"}};
  877. EXPECT_TRUE(layout->AddShaderOption(option));
  878. }
  879. layout->Finalize();
  880. return layout;
  881. }
  882. TEST_F(ShaderTests, ShaderOptionGroup_AccessEachBit_AllOtherOptionsUnspecified)
  883. {
  884. AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> allBitsOff;
  885. for (size_t i = 0; i < AZ::RPI::ShaderVariantKeyBitCount; ++i)
  886. {
  887. // Verify the assumption that bitset is initialized to all false
  888. EXPECT_FALSE(allBitsOff[i]);
  889. }
  890. for (size_t targetBit = 0; targetBit < AZ::RPI::ShaderVariantKeyBitCount; ++targetBit)
  891. {
  892. RPI::ShaderOptionGroup group(CreateOptionsLayoutWithAllBools());
  893. // Set target bit on, all other bits are unspecified
  894. group.SetValue(AZ::RPI::ShaderOptionIndex{targetBit}, AZ::RPI::ShaderOptionValue{1});
  895. for (int j = 0; j < AZ::RPI::ShaderVariantKeyBitCount; ++j)
  896. {
  897. if (j == targetBit)
  898. {
  899. EXPECT_TRUE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
  900. EXPECT_EQ(1, group.GetValue(AZ::RPI::ShaderOptionIndex{j}).GetIndex());
  901. }
  902. else
  903. {
  904. EXPECT_FALSE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
  905. }
  906. }
  907. AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> expected = allBitsOff;
  908. expected[targetBit] = true;
  909. EXPECT_EQ(expected, group.GetShaderVariantId().m_key);
  910. EXPECT_EQ(expected, group.GetShaderVariantId().m_mask);
  911. }
  912. }
  913. TEST_F(ShaderTests, ShaderOptionGroup_AccessEachBit_AllOtherOptionsTrue)
  914. {
  915. AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> allBitsOn;
  916. allBitsOn.set();
  917. for (size_t i = 0; i < AZ::RPI::ShaderVariantKeyBitCount; ++i)
  918. {
  919. EXPECT_TRUE(allBitsOn[i]);
  920. }
  921. for (size_t targetBit = 0; targetBit < AZ::RPI::ShaderVariantKeyBitCount; ++targetBit)
  922. {
  923. RPI::ShaderOptionGroup group(CreateOptionsLayoutWithAllBools());
  924. // Set all other bits on
  925. for (int j = 0; j < AZ::RPI::ShaderVariantKeyBitCount; ++j)
  926. {
  927. group.SetValue(AZ::RPI::ShaderOptionIndex{j}, AZ::RPI::ShaderOptionValue{1});
  928. }
  929. // Set the target bit off
  930. group.SetValue(AZ::RPI::ShaderOptionIndex{targetBit}, AZ::RPI::ShaderOptionValue{0});
  931. for (int j = 0; j < AZ::RPI::ShaderVariantKeyBitCount; ++j)
  932. {
  933. if (j == targetBit)
  934. {
  935. EXPECT_TRUE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
  936. EXPECT_EQ(0, group.GetValue(AZ::RPI::ShaderOptionIndex{j}).GetIndex());
  937. }
  938. else
  939. {
  940. EXPECT_TRUE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
  941. EXPECT_EQ(1, group.GetValue(AZ::RPI::ShaderOptionIndex{j}).GetIndex());
  942. }
  943. }
  944. AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> expected = allBitsOn;
  945. expected[targetBit] = false;
  946. EXPECT_EQ(expected, group.GetShaderVariantId().m_key);
  947. EXPECT_EQ(allBitsOn, group.GetShaderVariantId().m_mask);
  948. }
  949. }
  950. TEST_F(ShaderTests, ShaderOptionGroup_SetAllToDefaultValues)
  951. {
  952. using namespace AZ;
  953. RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
  954. EXPECT_FALSE(group.GetValue(Name{"Color"}).IsValid());
  955. EXPECT_FALSE(group.GetValue(Name{"Quality"}).IsValid());
  956. EXPECT_FALSE(group.GetValue(Name{"NumberSamples"}).IsValid());
  957. EXPECT_FALSE(group.GetValue(Name{"Raytracing"}).IsValid());
  958. group.SetAllToDefaultValues();
  959. EXPECT_EQ(13, group.GetValue(Name{"Color"}).GetIndex());
  960. EXPECT_EQ(0, group.GetValue(Name{"Quality"}).GetIndex());
  961. EXPECT_EQ(50, group.GetValue(Name{"NumberSamples"}).GetIndex());
  962. EXPECT_EQ(0, group.GetValue(Name{"Raytracing"}).GetIndex());
  963. }
  964. TEST_F(ShaderTests, ShaderOptionGroup_SetUnspecifiedToDefaultValues)
  965. {
  966. using namespace AZ;
  967. RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
  968. EXPECT_FALSE(group.GetValue(Name{"Color"}).IsValid());
  969. EXPECT_FALSE(group.GetValue(Name{"Quality"}).IsValid());
  970. EXPECT_FALSE(group.GetValue(Name{"NumberSamples"}).IsValid());
  971. EXPECT_FALSE(group.GetValue(Name{"Raytracing"}).IsValid());
  972. group.SetValue(Name{"Color"}, Name("Yellow"));
  973. group.SetValue(Name{"Raytracing"}, Name("On"));
  974. group.SetUnspecifiedToDefaultValues();
  975. EXPECT_EQ(11, group.GetValue(Name{"Color"}).GetIndex());
  976. EXPECT_EQ(0, group.GetValue(Name{"Quality"}).GetIndex());
  977. EXPECT_EQ(50, group.GetValue(Name{"NumberSamples"}).GetIndex());
  978. EXPECT_EQ(1, group.GetValue(Name{"Raytracing"}).GetIndex());
  979. }
  980. TEST_F(ShaderTests, ShaderOptionGroup_ToString)
  981. {
  982. using namespace AZ;
  983. RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
  984. group.SetValue(Name("Color"), Name("Silver")); // 7
  985. group.SetValue(Name("NumberSamples"), Name("50")); // 50
  986. group.SetValue(Name("Raytracing"), Name("On")); // 1
  987. EXPECT_STREQ("Color=7, Quality=?, NumberSamples=50, Raytracing=1", group.ToString().c_str());
  988. }
  989. TEST_F(ShaderTests, ShaderOptionGroupTest_Errors)
  990. {
  991. using namespace AZ::RPI;
  992. Ptr<ShaderOptionGroupLayout> layout = CreateShaderOptionLayout();
  993. ShaderOptionIndex colorIndex = layout->FindShaderOptionIndex(Name{ "Color" });
  994. ShaderOptionValue redValue = layout->GetShaderOption(colorIndex).FindValue(Name("Red"));
  995. RPI::ShaderOptionGroup group(layout);
  996. // Setting by option index and value index...
  997. AZ_TEST_START_TRACE_SUPPRESSION;
  998. EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, ShaderOptionValue{}));
  999. EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, redValue));
  1000. EXPECT_FALSE(group.SetValue(colorIndex, ShaderOptionValue{}));
  1001. AZ_TEST_STOP_TRACE_SUPPRESSION(3);
  1002. EXPECT_TRUE(group.SetValue(colorIndex, redValue));
  1003. // Setting by option name and value index...
  1004. AZ_TEST_START_TRACE_SUPPRESSION;
  1005. EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, ShaderOptionValue{}));
  1006. EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, redValue));
  1007. EXPECT_FALSE(group.SetValue(Name{ "Color" }, ShaderOptionValue{}));
  1008. AZ_TEST_STOP_TRACE_SUPPRESSION(3);
  1009. EXPECT_TRUE(group.SetValue(Name{ "Color" }, redValue));
  1010. // Setting by option index and value name...
  1011. AZ_TEST_START_TRACE_SUPPRESSION;
  1012. EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, Name{ "DoesNotExist" }));
  1013. EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, Name{ "Red" }));
  1014. EXPECT_FALSE(group.SetValue(colorIndex, Name{ "DoesNotExist" }));
  1015. AZ_TEST_STOP_TRACE_SUPPRESSION(3);
  1016. EXPECT_TRUE(group.SetValue(colorIndex, Name{ "Red" }));
  1017. // Setting by option name and value name...
  1018. AZ_TEST_START_TRACE_SUPPRESSION;
  1019. EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, Name{ "DoesNotExist" }));
  1020. EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, Name{ "Red" }));
  1021. EXPECT_FALSE(group.SetValue(Name{ "Color" }, Name{ "DoesNotExist" }));
  1022. AZ_TEST_STOP_TRACE_SUPPRESSION(3);
  1023. EXPECT_TRUE(group.SetValue(Name{ "Color" }, Name{ "Red" }));
  1024. // GetValue by option index...
  1025. AZ_TEST_START_TRACE_SUPPRESSION;
  1026. EXPECT_FALSE(group.GetValue(ShaderOptionIndex{}).IsValid());
  1027. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  1028. EXPECT_TRUE(group.GetValue(colorIndex).IsValid());
  1029. // GetValue by option name...
  1030. AZ_TEST_START_TRACE_SUPPRESSION;
  1031. EXPECT_FALSE(group.GetValue(Name{ "DoesNotExist" }).IsValid());
  1032. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  1033. EXPECT_TRUE(group.GetValue(Name{ "Color" }).IsValid());
  1034. // Clearing by option index...
  1035. AZ_TEST_START_TRACE_SUPPRESSION;
  1036. EXPECT_FALSE(group.ClearValue(ShaderOptionIndex{}));
  1037. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  1038. EXPECT_TRUE(group.ClearValue(colorIndex));
  1039. // Clearing by option name...
  1040. AZ_TEST_START_TRACE_SUPPRESSION;
  1041. EXPECT_FALSE(group.ClearValue(Name{ "DoesNotExist" }));
  1042. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  1043. EXPECT_TRUE(group.ClearValue(Name{ "Color" }));
  1044. }
  1045. TEST_F(ShaderTests, ShaderAsset_Baseline_Test)
  1046. {
  1047. using namespace AZ;
  1048. ValidateShaderAsset(CreateShaderAsset());
  1049. }
  1050. TEST_F(ShaderTests, ShaderAsset_PipelineStateType_VertexImpliesDraw)
  1051. {
  1052. AZ::RPI::ShaderAssetCreator creator;
  1053. BeginCreatingTestShaderAsset(creator, {RHI::ShaderStage::Vertex});
  1054. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1055. EXPECT_TRUE(shaderAsset);
  1056. EXPECT_EQ(shaderAsset->GetPipelineStateType(), RHI::PipelineStateType::Draw);
  1057. }
  1058. TEST_F(ShaderTests, ShaderAsset_PipelineStateType_ComputeImpliesDispatch)
  1059. {
  1060. AZ::RPI::ShaderAssetCreator creator;
  1061. BeginCreatingTestShaderAsset(creator, {AZ::RHI::ShaderStage::Compute});
  1062. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1063. EXPECT_TRUE(shaderAsset);
  1064. EXPECT_EQ(shaderAsset->GetPipelineStateType(), RHI::PipelineStateType::Dispatch);
  1065. }
  1066. TEST_F(ShaderTests, ShaderAsset_PipelineStateType_Error_DrawAndDispatch)
  1067. {
  1068. ErrorMessageFinder messageFinder("both Draw functions and Dispatch functions");
  1069. messageFinder.AddExpectedErrorMessage("Invalid root variant");
  1070. messageFinder.AddExpectedErrorMessage("Cannot continue building ShaderAsset because 1 error(s) reported");
  1071. AZ::RPI::ShaderAssetCreator creator;
  1072. BeginCreatingTestShaderAsset(creator,
  1073. {AZ::RHI::ShaderStage::Vertex, AZ::RHI::ShaderStage::Fragment, AZ::RHI::ShaderStage::Compute});
  1074. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1075. EXPECT_FALSE(shaderAsset);
  1076. }
  1077. TEST_F(ShaderTests, ShaderAsset_Error_FragmentFunctionRequiresVertexFunction)
  1078. {
  1079. ErrorMessageFinder messageFinder("fragment function but no vertex function");
  1080. messageFinder.AddExpectedErrorMessage("Invalid root variant");
  1081. messageFinder.AddExpectedErrorMessage("Cannot continue building ShaderAsset because 1 error(s) reported");
  1082. AZ::RPI::ShaderAssetCreator creator;
  1083. BeginCreatingTestShaderAsset(creator, {AZ::RHI::ShaderStage::Fragment});
  1084. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1085. messageFinder.CheckExpectedErrorsFound();
  1086. EXPECT_FALSE(shaderAsset);
  1087. }
  1088. TEST_F(ShaderTests, ShaderAsset_Error_GeometryFunctionRequiresVertexFunction)
  1089. {
  1090. ErrorMessageFinder messageFinder("geometry function but no vertex function");
  1091. messageFinder.AddExpectedErrorMessage("Invalid root variant");
  1092. messageFinder.AddExpectedErrorMessage("Cannot continue building ShaderAsset because 1 error(s) reported");
  1093. AZ::RPI::ShaderAssetCreator creator;
  1094. BeginCreatingTestShaderAsset(creator, { AZ::RHI::ShaderStage::Geometry });
  1095. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1096. messageFinder.CheckExpectedErrorsFound();
  1097. EXPECT_FALSE(shaderAsset);
  1098. }
  1099. TEST_F(ShaderTests, ShaderAsset_Serialize_Test)
  1100. {
  1101. using namespace AZ;
  1102. Data::Asset<RPI::ShaderAsset> shaderAsset = CreateShaderAsset();
  1103. ValidateShaderAsset(shaderAsset);
  1104. RPI::ShaderAssetTester tester(GetSerializeContext());
  1105. tester.SerializeOut(shaderAsset.Get());
  1106. Data::Asset<RPI::ShaderAsset> serializedShaderAsset = tester.SerializeInHelper(Uuid::CreateRandom());
  1107. ValidateShaderAsset(serializedShaderAsset);
  1108. }
  1109. TEST_F(ShaderTests, ShaderAsset_PipelineLayout_Missing_Test)
  1110. {
  1111. using namespace AZ;
  1112. m_pipelineLayoutDescriptor = nullptr;
  1113. AZ_TEST_START_TRACE_SUPPRESSION;
  1114. Data::Asset<RPI::ShaderAsset> shaderAsset = CreateShaderAsset();
  1115. AZ_TEST_STOP_TRACE_SUPPRESSION(2);
  1116. EXPECT_FALSE(shaderAsset);
  1117. }
  1118. TEST_F(ShaderTests, ShaderAsset_ShaderOptionGroupLayout_Mismatch_Test)
  1119. {
  1120. using namespace AZ;
  1121. const size_t indexToOmit = 0;
  1122. // Creates a shader option group layout assigned to the asset which doesn't match the
  1123. // one assigned to the the variants.
  1124. m_shaderOptionGroupLayoutForAsset = CreateShaderOptionLayout(RHI::Handle<size_t>(indexToOmit));
  1125. AZ_TEST_START_TRACE_SUPPRESSION;
  1126. Data::Asset<RPI::ShaderAsset> shaderAsset = CreateShaderAsset();
  1127. Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset = CreateShaderVariantTreeAssetForSearch(shaderAsset);
  1128. AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
  1129. EXPECT_FALSE(shaderVariantTreeAsset);
  1130. }
  1131. TEST_F(ShaderTests, ShaderAsset_DefaultShaderOptions)
  1132. {
  1133. using namespace AZ;
  1134. RPI::ShaderAssetCreator creator;
  1135. BeginCreatingTestShaderAsset(creator);
  1136. // Override two of the default values. The others will maintain the default value from the shader options layout, see SetUp().
  1137. creator.SetShaderOptionDefaultValue(Name{"Quality"}, Name{"Quality::Average"});
  1138. creator.SetShaderOptionDefaultValue(Name{"Raytracing"}, Name{"On"});
  1139. Data::Asset<RPI::ShaderAsset> shaderAssetWithShaderOptionOverrides = EndCreatingTestShaderAsset(creator);
  1140. // These options were overridden
  1141. EXPECT_EQ(3, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"Quality"}).GetIndex());
  1142. EXPECT_EQ(1, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"Raytracing"}).GetIndex());
  1143. // These options maintain their original default values
  1144. EXPECT_EQ(13, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"Color"}).GetIndex());
  1145. EXPECT_EQ(50, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"NumberSamples"}).GetIndex());
  1146. }
  1147. TEST_F(ShaderTests, Shader_Baseline_Test)
  1148. {
  1149. using namespace AZ;
  1150. Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(CreateShaderAsset());
  1151. ValidateShader(shader);
  1152. }
  1153. TEST_F(ShaderTests, ValidateShaderVariantIdMath)
  1154. {
  1155. RPI::ShaderVariantId idSmall;
  1156. RPI::ShaderVariantId idLarge;
  1157. RPI::ShaderVariantIdComparator idComparator;
  1158. idSmall.m_mask = RPI::ShaderVariantKey(15);
  1159. idLarge.m_mask = RPI::ShaderVariantKey(31);
  1160. idSmall.m_key = RPI::ShaderVariantKey(15);
  1161. idLarge.m_key = RPI::ShaderVariantKey(31);
  1162. EXPECT_TRUE(idComparator(idSmall, idLarge));
  1163. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
  1164. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1165. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
  1166. // The mask has precedence so the evaluation is the same as above
  1167. idSmall.m_key = RPI::ShaderVariantKey(31);
  1168. idLarge.m_key = RPI::ShaderVariantKey(15);
  1169. EXPECT_TRUE(idComparator(idSmall, idLarge));
  1170. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
  1171. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1172. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
  1173. // The mask has precedence so the evaluation is the same as above
  1174. idSmall.m_key = RPI::ShaderVariantKey(0);
  1175. idLarge.m_key = RPI::ShaderVariantKey(0);
  1176. EXPECT_TRUE(idComparator(idSmall, idLarge));
  1177. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
  1178. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1179. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
  1180. // The mask has precedence so the evaluation is the same as above
  1181. idSmall.m_key = RPI::ShaderVariantKey(63);
  1182. idLarge.m_key = RPI::ShaderVariantKey(63);
  1183. EXPECT_TRUE(idComparator(idSmall, idLarge));
  1184. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
  1185. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1186. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
  1187. // In the case where the mask are equal, the id's key should be used
  1188. idSmall.m_mask = RPI::ShaderVariantKey(31);
  1189. idLarge.m_mask = RPI::ShaderVariantKey(31);
  1190. idSmall.m_key = RPI::ShaderVariantKey(6);
  1191. idLarge.m_key = RPI::ShaderVariantKey(20);
  1192. EXPECT_TRUE(idComparator(idSmall, idLarge));
  1193. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
  1194. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1195. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
  1196. // The variant id is the same
  1197. idSmall.m_mask = RPI::ShaderVariantKey(31);
  1198. idLarge.m_mask = RPI::ShaderVariantKey(31);
  1199. idSmall.m_key = RPI::ShaderVariantKey(15);
  1200. idLarge.m_key = RPI::ShaderVariantKey(15);
  1201. EXPECT_FALSE(idComparator(idSmall, idLarge));
  1202. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), 0);
  1203. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1204. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 0);
  1205. // The variant id is the same
  1206. idSmall.m_mask = RPI::ShaderVariantKey(0);
  1207. idLarge.m_mask = RPI::ShaderVariantKey(0);
  1208. EXPECT_FALSE(idComparator(idSmall, idLarge));
  1209. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), 0);
  1210. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1211. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 0);
  1212. // If the mask is 0, the key has insignificant bits, the variant id is the same
  1213. idSmall.m_mask = RPI::ShaderVariantKey(0);
  1214. idLarge.m_mask = RPI::ShaderVariantKey(0);
  1215. idSmall.m_key = RPI::ShaderVariantKey(31);
  1216. idLarge.m_key = RPI::ShaderVariantKey(15);
  1217. EXPECT_FALSE(idComparator(idSmall, idLarge));
  1218. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), 0);
  1219. EXPECT_FALSE(idComparator(idLarge, idSmall));
  1220. EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 0);
  1221. }
  1222. TEST_F(ShaderTests, ValidateShaderVariantKeyFallbackPacking)
  1223. {
  1224. AZStd::vector<RPI::ShaderOptionValuePair> idList0;
  1225. idList0.push_back({ Name("Black"), RPI::ShaderOptionValue(0) }); // 1+ bit
  1226. idList0.push_back({ Name("Maroon"), RPI::ShaderOptionValue(1) }); // ...
  1227. idList0.push_back({ Name("Green"), RPI::ShaderOptionValue(2) }); // 2+ bits
  1228. idList0.push_back({ Name("Olive"), RPI::ShaderOptionValue(3) }); // ...
  1229. idList0.push_back({ Name("Navy"), RPI::ShaderOptionValue(4) }); // 3+ bits
  1230. idList0.push_back({ Name("Purple"), RPI::ShaderOptionValue(5) }); // ...
  1231. idList0.push_back({ Name("Teal"), RPI::ShaderOptionValue(6) }); // ...
  1232. idList0.push_back({ Name("Silver"), RPI::ShaderOptionValue(7) }); // ...
  1233. idList0.push_back({ Name("Gray"), RPI::ShaderOptionValue(8) }); // 4+ bits
  1234. idList0.push_back({ Name("Red"), RPI::ShaderOptionValue(9) }); // ...
  1235. idList0.push_back({ Name("Lime"), RPI::ShaderOptionValue(10) }); // ...
  1236. idList0.push_back({ Name("Yellow"), RPI::ShaderOptionValue(11) }); // ...
  1237. idList0.push_back({ Name("Blue"), RPI::ShaderOptionValue(12) }); // ...
  1238. idList0.push_back({ Name("Fuchsia"), RPI::ShaderOptionValue(13) }); // ...
  1239. idList0.push_back({ Name("Cyan"), RPI::ShaderOptionValue(14) }); // ...
  1240. idList0.push_back({ Name("White"), RPI::ShaderOptionValue(15) }); // ...
  1241. idList0.push_back({ Name("Beige"), RPI::ShaderOptionValue(16) }); // 5 bits!!
  1242. // Six descriptors with 5 bits each are 30 bits, but AZSLc will pack them within 32-bit boundaries, so
  1243. // every six descriptors will end up wasting 2 bits of register space.
  1244. // This test checks for values up to 256 bits
  1245. uint32_t bitOffset = 0;
  1246. uint32_t order = 0;
  1247. constexpr uint32_t descriptorsPerElement = 6;
  1248. constexpr uint32_t numberOfElements = RPI::ShaderVariantKeyBitCount / RPI::ShaderElementBitSize;
  1249. RPI::ShaderOptionDescriptor descriptor[numberOfElements * descriptorsPerElement];
  1250. RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
  1251. for (int i = 0; i < numberOfElements * descriptorsPerElement; i++)
  1252. {
  1253. std::stringstream ss;
  1254. ss << "Color" << i;
  1255. descriptor[i] = RPI::ShaderOptionDescriptor{ Name(ss.str().c_str()),
  1256. RPI::ShaderOptionType::Enumeration,
  1257. bitOffset,
  1258. order++,
  1259. idList0,
  1260. Name("Fuchsia") };
  1261. shaderOptionGroupLayout->AddShaderOption(descriptor[i]);
  1262. EXPECT_EQ(descriptor[i].GetBitCount(), 5);
  1263. bitOffset = descriptor[i].GetBitOffset() + descriptor[i].GetBitCount();
  1264. // This hack up-aligns the bit offset to match the AZSLc behavior
  1265. // (AZSLc respects a 32-bit boundary for any options used)
  1266. // It doesn't matter for the test itself since we read raw data
  1267. if (i % descriptorsPerElement == (descriptorsPerElement - 1))
  1268. {
  1269. bitOffset += 2;
  1270. }
  1271. }
  1272. shaderOptionGroupLayout->Finalize();
  1273. // Create and test a few ShaderOptionGroup-s
  1274. // This simple test matches the expected padding for AZSLc and should only be updated
  1275. // if AZSLc.exe changes the shader variant key fallback mask.
  1276. auto shaderOptionGroup = RPI::ShaderOptionGroup(shaderOptionGroupLayout);
  1277. // ShaderVariantKey is 32 or more bits
  1278. if constexpr (numberOfElements >= 1)
  1279. {
  1280. shaderOptionGroup.SetValue(AZ::Name("Color0"), AZ::Name("Beige")); // 16
  1281. shaderOptionGroup.SetValue(AZ::Name("Color1"), AZ::Name("Olive")); // 3
  1282. shaderOptionGroup.SetValue(AZ::Name("Color2"), AZ::Name("Navy")); // 4
  1283. shaderOptionGroup.SetValue(AZ::Name("Color3"), AZ::Name("Teal")); // 6
  1284. shaderOptionGroup.SetValue(AZ::Name("Color4"), AZ::Name("Lime")); // 10
  1285. shaderOptionGroup.SetValue(AZ::Name("Color5"), AZ::Name("Fuchsia")); // 13
  1286. }
  1287. // ShaderVariantKey is 64 or more bits
  1288. if constexpr (numberOfElements >= 2)
  1289. {
  1290. shaderOptionGroup.SetValue(AZ::Name("Color6"), AZ::Name("Olive")); // 3
  1291. shaderOptionGroup.SetValue(AZ::Name("Color7"), AZ::Name("Beige")); // 16
  1292. shaderOptionGroup.SetValue(AZ::Name("Color8"), AZ::Name("Navy")); // 4
  1293. shaderOptionGroup.SetValue(AZ::Name("Color9"), AZ::Name("Teal")); // 6
  1294. shaderOptionGroup.SetValue(AZ::Name("Color10"), AZ::Name("Lime")); // 10
  1295. shaderOptionGroup.SetValue(AZ::Name("Color11"), AZ::Name("Fuchsia")); // 13
  1296. }
  1297. // ShaderVariantKey is 96 or more bits
  1298. if constexpr (numberOfElements >= 3)
  1299. {
  1300. shaderOptionGroup.SetValue(AZ::Name("Color12"), AZ::Name("Navy")); // 4
  1301. shaderOptionGroup.SetValue(AZ::Name("Color13"), AZ::Name("Beige")); // 16
  1302. shaderOptionGroup.SetValue(AZ::Name("Color14"), AZ::Name("Olive")); // 3
  1303. shaderOptionGroup.SetValue(AZ::Name("Color15"), AZ::Name("Teal")); // 6
  1304. shaderOptionGroup.SetValue(AZ::Name("Color16"), AZ::Name("Lime")); // 10
  1305. shaderOptionGroup.SetValue(AZ::Name("Color17"), AZ::Name("Fuchsia")); // 13
  1306. }
  1307. // ShaderVariantKey is 128 or more bits
  1308. if constexpr (numberOfElements >= 4)
  1309. {
  1310. shaderOptionGroup.SetValue(AZ::Name("Color18"), AZ::Name("Teal")); // 6
  1311. shaderOptionGroup.SetValue(AZ::Name("Color19"), AZ::Name("Beige")); // 16
  1312. shaderOptionGroup.SetValue(AZ::Name("Color20"), AZ::Name("Olive")); // 3
  1313. shaderOptionGroup.SetValue(AZ::Name("Color21"), AZ::Name("Navy")); // 4
  1314. shaderOptionGroup.SetValue(AZ::Name("Color22"), AZ::Name("Lime")); // 10
  1315. shaderOptionGroup.SetValue(AZ::Name("Color23"), AZ::Name("Fuchsia")); // 13
  1316. }
  1317. // ShaderVariantKey is 160 or more bits
  1318. if constexpr (numberOfElements >= 5)
  1319. {
  1320. shaderOptionGroup.SetValue(AZ::Name("Color24"), AZ::Name("Navy")); // 4
  1321. shaderOptionGroup.SetValue(AZ::Name("Color25"), AZ::Name("Teal")); // 6
  1322. shaderOptionGroup.SetValue(AZ::Name("Color26"), AZ::Name("Lime")); // 10
  1323. shaderOptionGroup.SetValue(AZ::Name("Color27"), AZ::Name("Fuchsia")); // 13
  1324. shaderOptionGroup.SetValue(AZ::Name("Color28"), AZ::Name("Beige")); // 16
  1325. shaderOptionGroup.SetValue(AZ::Name("Color29"), AZ::Name("Olive")); // 3
  1326. }
  1327. // ShaderVariantKey is 192 or more bits
  1328. if constexpr (numberOfElements >= 6)
  1329. {
  1330. shaderOptionGroup.SetValue(AZ::Name("Color30"), AZ::Name("Teal")); // 6
  1331. shaderOptionGroup.SetValue(AZ::Name("Color31"), AZ::Name("Lime")); // 10
  1332. shaderOptionGroup.SetValue(AZ::Name("Color32"), AZ::Name("Fuchsia")); // 13
  1333. shaderOptionGroup.SetValue(AZ::Name("Color33"), AZ::Name("Beige")); // 16
  1334. shaderOptionGroup.SetValue(AZ::Name("Color34"), AZ::Name("Olive")); // 3
  1335. shaderOptionGroup.SetValue(AZ::Name("Color35"), AZ::Name("Navy")); // 4
  1336. }
  1337. // ShaderVariantKey is 224 or more bits
  1338. if constexpr (numberOfElements >= 7)
  1339. {
  1340. shaderOptionGroup.SetValue(AZ::Name("Color36"), AZ::Name("Lime")); // 10
  1341. shaderOptionGroup.SetValue(AZ::Name("Color37"), AZ::Name("Fuchsia")); // 13
  1342. shaderOptionGroup.SetValue(AZ::Name("Color38"), AZ::Name("Beige")); // 16
  1343. shaderOptionGroup.SetValue(AZ::Name("Color39"), AZ::Name("Olive")); // 3
  1344. shaderOptionGroup.SetValue(AZ::Name("Color40"), AZ::Name("Navy")); // 4
  1345. shaderOptionGroup.SetValue(AZ::Name("Color41"), AZ::Name("Teal")); // 6
  1346. }
  1347. // ShaderVariantKey is 256 or more bits
  1348. if constexpr (numberOfElements >= 8)
  1349. {
  1350. shaderOptionGroup.SetValue(AZ::Name("Color42"), AZ::Name("Fuchsia")); // 13
  1351. shaderOptionGroup.SetValue(AZ::Name("Color43"), AZ::Name("Beige")); // 16
  1352. shaderOptionGroup.SetValue(AZ::Name("Color44"), AZ::Name("Olive")); // 3
  1353. shaderOptionGroup.SetValue(AZ::Name("Color45"), AZ::Name("Navy")); // 4
  1354. shaderOptionGroup.SetValue(AZ::Name("Color46"), AZ::Name("Teal")); // 6
  1355. shaderOptionGroup.SetValue(AZ::Name("Color47"), AZ::Name("Lime")); // 10
  1356. }
  1357. uint32_t fallbackValue[RPI::ShaderVariantKeyAlignedBitCount / RPI::ShaderElementBitSize];
  1358. memcpy(fallbackValue, shaderOptionGroup.GetShaderVariantId().m_key.data(), RPI::ShaderVariantKeyBitCount / 8);
  1359. if constexpr (numberOfElements > 0)
  1360. {
  1361. EXPECT_EQ(fallbackValue[0], 0x1aa31070);
  1362. }
  1363. if constexpr (numberOfElements > 1)
  1364. {
  1365. EXPECT_EQ(fallbackValue[1], 0x1aa31203);
  1366. }
  1367. if constexpr (numberOfElements > 2)
  1368. {
  1369. EXPECT_EQ(fallbackValue[2], 0x1aa30e04);
  1370. }
  1371. if constexpr (numberOfElements > 3)
  1372. {
  1373. EXPECT_EQ(fallbackValue[3], 0x1aa20e06);
  1374. }
  1375. if constexpr (numberOfElements > 4)
  1376. {
  1377. EXPECT_EQ(fallbackValue[4], 0x0706a8c4);
  1378. }
  1379. if constexpr (numberOfElements > 5)
  1380. {
  1381. EXPECT_EQ(fallbackValue[5], 0x08383546);
  1382. }
  1383. if constexpr (numberOfElements > 6)
  1384. {
  1385. EXPECT_EQ(fallbackValue[6], 0x0c41c1aa);
  1386. }
  1387. if constexpr (numberOfElements > 7)
  1388. {
  1389. EXPECT_EQ(fallbackValue[7], 0x14620e0d);
  1390. }
  1391. }
  1392. TEST_F(ShaderTests, ShaderAsset_ValidateSearch)
  1393. {
  1394. using namespace AZ;
  1395. using namespace AZ::RPI;
  1396. auto shaderAsset = CreateShaderAsset();
  1397. auto shaderVariantTreeAsset = CreateShaderVariantTreeAssetForSearch(shaderAsset);
  1398. // We expect the following composition:
  1399. // Index 0 - []
  1400. // Index 1 - [Fuchsia]
  1401. // Index 2 - [Fuchsia, Quality::Auto]
  1402. // Index 3 - [Fuchsia, Quality::Auto, 50]
  1403. // Index 4 - [Fuchsia, Quality::Auto, 50, Off]
  1404. // Index 5 - [Fuchsia, Quality::Auto, 50, On]
  1405. // Index 6 - [Teal]
  1406. // Index 7 - [Teal, Quality::Sublime]
  1407. // Let's search it!
  1408. RPI::ShaderOptionGroup shaderOptionGroup(m_shaderOptionGroupLayoutForVariants);
  1409. const uint32_t stableId0 = 0;
  1410. const uint32_t stableId1 = 1;
  1411. const uint32_t stableId2 = 2;
  1412. const uint32_t stableId3 = 3;
  1413. const uint32_t stableId4 = 4;
  1414. const uint32_t stableId5 = 5;
  1415. const uint32_t stableId6 = 6;
  1416. const uint32_t stableId7 = 7;
  1417. // Index 0 - []
  1418. const auto& result0 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1419. EXPECT_TRUE(result0.IsRoot());
  1420. EXPECT_FALSE(result0.IsFullyBaked());
  1421. EXPECT_EQ(result0.GetStableId().GetIndex(), stableId0);
  1422. // Index 1 - [Fuchsia]
  1423. shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
  1424. const auto& result1 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1425. EXPECT_FALSE(result1.IsRoot());
  1426. EXPECT_FALSE(result1.IsFullyBaked());
  1427. EXPECT_EQ(result1.GetStableId().GetIndex(), stableId1);
  1428. // Index 2 - [Fuchsia, Quality::Auto]
  1429. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Auto"));
  1430. const auto& result2 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1431. EXPECT_FALSE(result2.IsRoot());
  1432. EXPECT_FALSE(result2.IsFullyBaked());
  1433. EXPECT_EQ(result2.GetStableId().GetIndex(), stableId2);
  1434. // Index 3 - [Fuchsia, Quality::Auto, 50]
  1435. shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
  1436. const auto& result3 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1437. EXPECT_FALSE(result3.IsRoot());
  1438. EXPECT_FALSE(result3.IsFullyBaked());
  1439. EXPECT_EQ(result3.GetStableId().GetIndex(), stableId3);
  1440. // Index 4 - [Fuchsia, Quality::Auto, 50, Off]
  1441. shaderOptionGroup.SetValue(Name("Raytracing"), Name("Off"));
  1442. const auto& result4 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1443. EXPECT_FALSE(result4.IsRoot());
  1444. EXPECT_TRUE(result4.IsFullyBaked());
  1445. EXPECT_EQ(result4.GetStableId().GetIndex(), stableId4);
  1446. // Index 5 - [Fuchsia, Quality::Auto, 50, On]
  1447. shaderOptionGroup.SetValue(Name("Raytracing"), Name("On"));
  1448. const auto& result5 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1449. EXPECT_FALSE(result5.IsRoot());
  1450. EXPECT_TRUE(result5.IsFullyBaked());
  1451. EXPECT_EQ(result5.GetStableId().GetIndex(), stableId5);
  1452. shaderOptionGroup.Clear();
  1453. // Index 6 - [Teal]
  1454. shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
  1455. const auto& result6 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1456. EXPECT_FALSE(result6.IsRoot());
  1457. EXPECT_FALSE(result6.IsFullyBaked());
  1458. EXPECT_EQ(result6.GetStableId().GetIndex(), stableId6);
  1459. // Index 7 - [Teal, Quality::Sublime]
  1460. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
  1461. const auto& result7 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1462. EXPECT_FALSE(result7.IsRoot());
  1463. EXPECT_FALSE(result7.IsFullyBaked());
  1464. EXPECT_EQ(result7.GetStableId().GetIndex(), stableId7);
  1465. /*
  1466. All searches so far found exactly the node we were looking for
  1467. The next couple of searches will not find the requested node
  1468. and will instead default to its parent, up the tree to the root
  1469. [] [Root]
  1470. / \
  1471. [Color] [Teal] [Fuchsia]
  1472. / \
  1473. [Quality] [Sublime] [Auto]
  1474. /
  1475. [NumberSamples] [50]
  1476. / \
  1477. [Raytracing] [On] [Off]
  1478. */
  1479. // ----------------------------------------
  1480. // [Quality::Poor]
  1481. shaderOptionGroup.Clear();
  1482. shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
  1483. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Poor"));
  1484. // This node doesn't exist, but setting the quality forced Color to its default value, so we expect to get:
  1485. // Index 1 - [Fuchsia]
  1486. const auto& result8 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1487. EXPECT_FALSE(result8.IsRoot());
  1488. EXPECT_FALSE(result8.IsFullyBaked());
  1489. EXPECT_EQ(result8.GetStableId().GetIndex(), stableId1);
  1490. // ----------------------------------------
  1491. // [Teal, Quality::Poor]
  1492. shaderOptionGroup.Clear();
  1493. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Poor"));
  1494. shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
  1495. // This node doesn't exist, but we have set both Color and Quality so we expect to get:
  1496. // Index 6 - [Teal]
  1497. const auto& result9 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1498. EXPECT_FALSE(result9.IsRoot());
  1499. EXPECT_FALSE(result9.IsFullyBaked());
  1500. EXPECT_EQ(result9.GetStableId().GetIndex(), stableId6);
  1501. // ----------------------------------------
  1502. // [Navy, Quality::Good]
  1503. shaderOptionGroup.Clear();
  1504. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Good"));
  1505. shaderOptionGroup.SetValue(Name("Color"), Name("Navy"));
  1506. // This node doesn't exist (Good Navy), its parent (Navy) doesn't exist either so we expect to get the root:
  1507. // Index 0 - []
  1508. const auto& resultA = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1509. EXPECT_TRUE(resultA.IsRoot());
  1510. EXPECT_FALSE(resultA.IsFullyBaked());
  1511. EXPECT_EQ(resultA.GetStableId().GetIndex(), stableId0);
  1512. // ----------------------------------------
  1513. // [Teal, Quality::Sublime, 50, Off] - Test 1/3
  1514. shaderOptionGroup.Clear();
  1515. shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
  1516. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
  1517. shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
  1518. shaderOptionGroup.SetValue(Name("Raytracing"), Name("Off"));
  1519. // No specialized nodes exist under (Teal, Sublime) so we expect to get that:
  1520. // Index 7 - [Teal, Quality::Sublime]
  1521. const auto& resultB = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1522. EXPECT_FALSE(resultB.IsRoot());
  1523. EXPECT_FALSE(resultB.IsFullyBaked());
  1524. EXPECT_EQ(resultB.GetStableId().GetIndex(), stableId7);
  1525. // ----------------------------------------
  1526. // [Teal, Quality::Sublime, 50, On] - Test 2/3
  1527. shaderOptionGroup.Clear();
  1528. shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
  1529. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
  1530. shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
  1531. shaderOptionGroup.SetValue(Name("Raytracing"), Name("On"));
  1532. // No specialized nodes exist under (Teal, Sublime) so we expect to get that:
  1533. // Index 7 - [Teal, Quality::Sublime]
  1534. const auto& resultC = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1535. EXPECT_FALSE(resultC.IsRoot());
  1536. EXPECT_FALSE(resultC.IsFullyBaked());
  1537. EXPECT_EQ(resultC.GetStableId().GetIndex(), stableId7);
  1538. // ----------------------------------------
  1539. // [Teal, Quality::Sublime, 150] - Test 3/3
  1540. shaderOptionGroup.Clear();
  1541. shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
  1542. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
  1543. shaderOptionGroup.SetValue(Name("NumberSamples"), Name("150"));
  1544. // No specialized nodes exist under (Teal, Sublime) so we expect to get that:
  1545. // Index 7 - [Teal, Quality::Sublime]
  1546. const auto& resultD = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1547. EXPECT_FALSE(resultD.IsRoot());
  1548. EXPECT_FALSE(resultD.IsFullyBaked());
  1549. EXPECT_EQ(resultD.GetStableId().GetIndex(), stableId7);
  1550. // ----------------------------------------
  1551. // [120]
  1552. shaderOptionGroup.Clear();
  1553. shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
  1554. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Auto"));
  1555. shaderOptionGroup.SetValue(Name("NumberSamples"), Name("120"));
  1556. // The node (Fuchsia, Auto, 120) doesn't exist - note that the higher order options assume their default values. We get:
  1557. // Index 2 - [Fuchsia, Quality::Auto]
  1558. const auto& resultE = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1559. EXPECT_FALSE(resultE.IsRoot());
  1560. EXPECT_FALSE(resultE.IsFullyBaked());
  1561. EXPECT_EQ(resultE.GetStableId().GetIndex(), stableId2);
  1562. // ----------------------------------------
  1563. // [50]
  1564. shaderOptionGroup.Clear();
  1565. shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
  1566. shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Auto"));
  1567. shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
  1568. // ----------------------------------------
  1569. shaderOptionGroup.SetValue(Name("Raytracing"), Name("Off"));
  1570. // Index 4 - [Fuchsia, Quality::Auto, 50, Off]
  1571. const auto& resultF = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1572. EXPECT_FALSE(resultF.IsRoot());
  1573. EXPECT_TRUE(resultF.IsFullyBaked());
  1574. EXPECT_EQ(resultF.GetStableId().GetIndex(), stableId4);
  1575. shaderOptionGroup.SetValue(Name("Raytracing"), Name("On"));
  1576. // Index 5 - [Fuchsia, Quality::Auto, 50, On]
  1577. const auto& resultG = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
  1578. EXPECT_FALSE(resultG.IsRoot());
  1579. EXPECT_TRUE(resultG.IsFullyBaked());
  1580. EXPECT_EQ(resultG.GetStableId().GetIndex(), stableId5);
  1581. }
  1582. TEST_F(ShaderTests, ShaderAsset_SpecializationConstants)
  1583. {
  1584. {
  1585. AZ::RPI::ShaderAssetCreator creator;
  1586. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::None);
  1587. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1588. EXPECT_FALSE(shaderAsset->UseSpecializationConstants());
  1589. EXPECT_FALSE(shaderAsset->IsFullySpecialized());
  1590. }
  1591. {
  1592. AZ::RPI::ShaderAssetCreator creator;
  1593. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::Partial);
  1594. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1595. EXPECT_TRUE(shaderAsset->UseSpecializationConstants());
  1596. EXPECT_FALSE(shaderAsset->IsFullySpecialized());
  1597. }
  1598. {
  1599. AZ::RPI::ShaderAssetCreator creator;
  1600. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::Full);
  1601. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1602. EXPECT_TRUE(shaderAsset->UseSpecializationConstants());
  1603. EXPECT_TRUE(shaderAsset->IsFullySpecialized());
  1604. }
  1605. m_shaderOptionGroupLayoutForAssetFullSpecialization = m_shaderOptionGroupLayoutForAsset;
  1606. {
  1607. AZ::RPI::ShaderAssetCreator creator;
  1608. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::Full);
  1609. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1610. EXPECT_FALSE(shaderAsset->UseSpecializationConstants());
  1611. EXPECT_FALSE(shaderAsset->IsFullySpecialized());
  1612. }
  1613. }
  1614. TEST_F(ShaderTests, ShaderVariantAsset_IsFullyBaked)
  1615. {
  1616. using namespace AZ;
  1617. using namespace AZ::RPI;
  1618. ShaderOptionGroup shaderOptions{m_shaderOptionGroupLayoutForAsset};
  1619. Data::Asset<ShaderVariantAsset> shaderVariantAsset;
  1620. shaderVariantAsset = CreateTestShaderVariantAsset(shaderOptions.GetShaderVariantId(), RPI::ShaderVariantStableId{0}, false);
  1621. EXPECT_FALSE(shaderVariantAsset->IsFullyBaked());
  1622. EXPECT_FALSE(ShaderOptionGroup(m_shaderOptionGroupLayoutForAsset, shaderVariantAsset->GetShaderVariantId()).IsFullySpecified());
  1623. shaderOptions.SetValue(AZ::Name{"Color"}, AZ::Name{"Yellow"});
  1624. shaderOptions.SetValue(AZ::Name{"Quality"}, AZ::Name{"Quality::Average"});
  1625. shaderOptions.SetValue(AZ::Name{"NumberSamples"}, AZ::Name{"100"});
  1626. shaderOptions.SetValue(AZ::Name{"Raytracing"}, AZ::Name{"On"});
  1627. shaderVariantAsset = CreateTestShaderVariantAsset(shaderOptions.GetShaderVariantId(), RPI::ShaderVariantStableId{0}, true);
  1628. EXPECT_TRUE(shaderVariantAsset->IsFullyBaked());
  1629. EXPECT_TRUE(ShaderOptionGroup(m_shaderOptionGroupLayoutForAsset, shaderVariantAsset->GetShaderVariantId()).IsFullySpecified());
  1630. shaderOptions.ClearValue(AZ::Name{"NumberSamples"});
  1631. shaderVariantAsset = CreateTestShaderVariantAsset(shaderOptions.GetShaderVariantId(), RPI::ShaderVariantStableId{0}, false);
  1632. EXPECT_FALSE(shaderVariantAsset->IsFullyBaked());
  1633. EXPECT_FALSE(ShaderOptionGroup(m_shaderOptionGroupLayoutForAsset, shaderVariantAsset->GetShaderVariantId()).IsFullySpecified());
  1634. }
  1635. TEST_F(ShaderTests, ShaderVariantAsset_IsFullySpecialized)
  1636. {
  1637. using namespace AZ;
  1638. using namespace AZ::RPI;
  1639. {
  1640. AZ::RPI::ShaderAssetCreator creator;
  1641. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::None);
  1642. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1643. Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(shaderAsset);
  1644. const RPI::ShaderVariant& rootShaderVariant = shader->GetVariant(RPI::ShaderVariantStableId{ 0 });
  1645. EXPECT_TRUE(rootShaderVariant.UseKeyFallback());
  1646. EXPECT_FALSE(rootShaderVariant.IsFullySpecialized());
  1647. EXPECT_FALSE(rootShaderVariant.UseSpecializationConstants());
  1648. }
  1649. {
  1650. AZ::RPI::ShaderAssetCreator creator;
  1651. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::Partial);
  1652. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1653. Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(shaderAsset);
  1654. const RPI::ShaderVariant& rootShaderVariant = shader->GetVariant(RPI::ShaderVariantStableId{ 0 });
  1655. EXPECT_TRUE(rootShaderVariant.UseKeyFallback());
  1656. EXPECT_FALSE(rootShaderVariant.IsFullySpecialized());
  1657. EXPECT_TRUE(rootShaderVariant.UseSpecializationConstants());
  1658. }
  1659. {
  1660. AZ::RPI::ShaderAssetCreator creator;
  1661. BeginCreatingTestShaderAsset(creator, { RHI::ShaderStage::Compute }, SpecializationType::Full);
  1662. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
  1663. Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(shaderAsset);
  1664. const RPI::ShaderVariant& rootShaderVariant = shader->GetVariant(RPI::ShaderVariantStableId{ 0 });
  1665. EXPECT_FALSE(rootShaderVariant.UseKeyFallback());
  1666. EXPECT_TRUE(rootShaderVariant.IsFullySpecialized());
  1667. EXPECT_TRUE(rootShaderVariant.UseSpecializationConstants());
  1668. }
  1669. }
  1670. }