| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <AzTest/AzTest.h>
- #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
- #include <Atom/RHI.Reflect/ShaderStageFunction.h>
- #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
- #include <Atom/RPI.Reflect/Shader/ShaderAssetCreator.h>
- #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
- #include <Atom/RPI.Edit/Shader/ShaderVariantTreeAssetCreator.h>
- #include <Atom/RPI.Edit/Shader/ShaderVariantAssetCreator.h>
- #include <Atom/RHI/RHISystemInterface.h>
- #include <Atom/RPI.Public/Shader/Shader.h>
- #include <Common/RPITestFixture.h>
- #include <Common/ErrorMessageFinder.h>
- #include <Common/SerializeTester.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/Utils/TypeHash.h>
- #include <AzCore/Math/Random.h>
- #include <AzCore/std/string/conversions.h>
- namespace AZ
- {
- namespace RPI
- {
- /// This length represents the up-aligned shader variant key length in respect to the shader register space
- /// AZSLc aligns all keys up to a register length and this constant emulates that requirement
- static constexpr uint32_t ShaderVariantKeyAlignedBitCount = (ShaderVariantKeyBitCount % ShaderRegisterBitSize == 0) ?
- ShaderVariantKeyBitCount :
- ShaderVariantKeyBitCount + (ShaderRegisterBitSize - ShaderVariantKeyBitCount % ShaderRegisterBitSize);
- class ShaderAssetTester
- : public UnitTest::SerializeTester<ShaderAsset>
- {
- using Base = UnitTest::SerializeTester<ShaderAsset>;
- public:
- ShaderAssetTester(AZ::SerializeContext* serializeContext)
- : Base(serializeContext)
- {}
- AZ::Data::Asset<ShaderAsset> SerializeInHelper(const AZ::Data::AssetId& assetId)
- {
- AZ::Data::Asset<ShaderAsset> asset = Base::SerializeIn(assetId);
- asset->SelectShaderApiData();
- asset->SetReady();
- return asset;
- }
- };
- }
- }
- namespace UnitTest
- {
- using namespace AZ;
- using ShaderByteCode = AZStd::vector<uint8_t>;
- class TestPipelineLayoutDescriptor
- : public AZ::RHI::PipelineLayoutDescriptor
- {
- public:
- AZ_RTTI(TestPipelineLayoutDescriptor, "{B226636F-7C85-4500-B499-26C112D1128B}", AZ::RHI::PipelineLayoutDescriptor);
- static void Reflect(AZ::ReflectContext* context)
- {
- if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<TestPipelineLayoutDescriptor, AZ::RHI::PipelineLayoutDescriptor>()
- ->Version(1)
- ;
- }
- }
- static AZ::RHI::Ptr<TestPipelineLayoutDescriptor> Create()
- {
- return aznew TestPipelineLayoutDescriptor;
- }
- };
- class TestShaderStageFunction
- : public AZ::RHI::ShaderStageFunction
- {
- public:
- AZ_RTTI(TestShaderStageFunction, "{1BAEE536-96CA-4AEB-BA73-D5D72EE35B45}", AZ::RHI::ShaderStageFunction);
- AZ_CLASS_ALLOCATOR(TestShaderStageFunction, AZ::SystemAllocator)
- static void Reflect(AZ::ReflectContext* context)
- {
- if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<TestShaderStageFunction, AZ::RHI::ShaderStageFunction>()
- ->Version(1)
- ->Field("m_byteCode", &TestShaderStageFunction::m_byteCode)
- ->Field("m_index", &TestShaderStageFunction::m_index)
- ;
- }
- }
- TestShaderStageFunction() = default;
- explicit TestShaderStageFunction(AZ::RHI::ShaderStage shaderStage)
- : AZ::RHI::ShaderStageFunction(shaderStage)
- {}
- void SetIndex(uint32_t index)
- {
- m_index = index;
- }
- int32_t m_index;
- ShaderByteCode m_byteCode;
- private:
- AZ::RHI::ResultCode FinalizeInternal() override
- {
- SetHash(AZ::TypeHash64(reinterpret_cast<const uint8_t*>(m_byteCode.data()), m_byteCode.size()));
- return AZ::RHI::ResultCode::Success;
- }
- };
- class ShaderTests
- : public RPITestFixture
- {
- protected:
- void SetUp() override
- {
- using namespace AZ;
- RPITestFixture::SetUp();
- auto* serializeContext = GetSerializeContext();
- TestPipelineLayoutDescriptor::Reflect(serializeContext);
- TestShaderStageFunction::Reflect(serializeContext);
- // Example of unscoped enum
- AZStd::vector<RPI::ShaderOptionValuePair> idList0;
- idList0.push_back({ Name("Black"), RPI::ShaderOptionValue(0) }); // 1+ bit
- idList0.push_back({ Name("Maroon"), RPI::ShaderOptionValue(1) }); // ...
- idList0.push_back({ Name("Green"), RPI::ShaderOptionValue(2) }); // 2+ bits
- idList0.push_back({ Name("Olive"), RPI::ShaderOptionValue(3) }); // ...
- idList0.push_back({ Name("Navy"), RPI::ShaderOptionValue(4) }); // 3+ bits
- idList0.push_back({ Name("Purple"), RPI::ShaderOptionValue(5) }); // ...
- idList0.push_back({ Name("Teal"), RPI::ShaderOptionValue(6) }); // ...
- idList0.push_back({ Name("Silver"), RPI::ShaderOptionValue(7) }); // ...
- idList0.push_back({ Name("Gray"), RPI::ShaderOptionValue(8) }); // 4+ bits
- idList0.push_back({ Name("Red"), RPI::ShaderOptionValue(9) }); // ...
- idList0.push_back({ Name("Lime"), RPI::ShaderOptionValue(10) }); // ...
- idList0.push_back({ Name("Yellow"), RPI::ShaderOptionValue(11) }); // ...
- idList0.push_back({ Name("Blue"), RPI::ShaderOptionValue(12) }); // ...
- idList0.push_back({ Name("Fuchsia"), RPI::ShaderOptionValue(13) }); // ...
- idList0.push_back({ Name("Cyan"), RPI::ShaderOptionValue(14) }); // ...
- idList0.push_back({ Name("White"), RPI::ShaderOptionValue(15) }); // ...
- uint32_t bitOffset = 0;
- uint32_t order = 0;
- m_bindings[0] = RPI::ShaderOptionDescriptor{ Name("Color"),
- RPI::ShaderOptionType::Enumeration,
- bitOffset,
- order++,
- idList0,
- Name("Fuchsia") };
- bitOffset = m_bindings[0].GetBitOffset() + m_bindings[0].GetBitCount();
- // Example of scoped enum - the only difference is that enumerators are qualified
- AZStd::vector<RPI::ShaderOptionValuePair> idList1;
- idList1.push_back({ Name("Quality::Auto"), RPI::ShaderOptionValue(0) }); // 1+ bit
- idList1.push_back({ Name("Quality::Poor"), RPI::ShaderOptionValue(1) }); // ...
- idList1.push_back({ Name("Quality::Low"), RPI::ShaderOptionValue(2) }); // 2+ bits
- idList1.push_back({ Name("Quality::Average"), RPI::ShaderOptionValue(3) }); // ...
- idList1.push_back({ Name("Quality::Good"), RPI::ShaderOptionValue(4) }); // 3+ bits
- idList1.push_back({ Name("Quality::High"), RPI::ShaderOptionValue(5) }); // ...
- idList1.push_back({ Name("Quality::Ultra"), RPI::ShaderOptionValue(6) }); // ...
- idList1.push_back({ Name("Quality::Sublime"), RPI::ShaderOptionValue(7) }); // ...
- m_bindings[1] = RPI::ShaderOptionDescriptor{ Name("Quality"),
- RPI::ShaderOptionType::Enumeration,
- bitOffset,
- order++,
- idList1,
- Name("Quality::Auto") };
- bitOffset = m_bindings[1].GetBitOffset() + m_bindings[1].GetBitCount();
- // Example of integer range. It only requires two values, min and max. The name id-s are expected to match the numericla value.
- AZStd::vector<RPI::ShaderOptionValuePair> idList2;
- idList2.push_back({ Name("5"), RPI::ShaderOptionValue(5) }); // 1+ bit
- idList2.push_back({ Name("200"), RPI::ShaderOptionValue(200) }); // 8+ bits
- 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
- m_bindings[2] = RPI::ShaderOptionDescriptor{ Name("NumberSamples"),
- RPI::ShaderOptionType::IntegerRange,
- bitOffset,
- order++,
- idList2,
- Name("50") };
- bitOffset = m_bindings[2].GetBitOffset() + m_bindings[2].GetBitCount();
- // Example of boolean. By standard, the first value should be false (0).
- AZStd::vector<RPI::ShaderOptionValuePair> idList3;
- idList3.push_back({ Name("Off"), RPI::ShaderOptionValue(0) }); // 1+ bit
- idList3.push_back({ Name("On"), RPI::ShaderOptionValue(1) }); // ...
- m_bindings[3] = RPI::ShaderOptionDescriptor{ Name("Raytracing"),
- RPI::ShaderOptionType::Boolean,
- bitOffset,
- order++,
- idList3,
- Name("Off") };
- bitOffset = m_bindings[3].GetBitOffset() + m_bindings[3].GetBitCount();
- m_name = Name("TestName");
- m_drawListName = Name("DrawListTagName");
- m_pipelineLayoutDescriptor = TestPipelineLayoutDescriptor::Create();
- m_shaderOptionGroupLayoutForAsset = CreateShaderOptionLayout();
- m_shaderOptionGroupLayoutForVariants = m_shaderOptionGroupLayoutForAsset;
- // Just set up a couple values, not the whole struct, for some basic checking later that the struct is copied.
- m_renderStates.m_rasterState.m_fillMode = RHI::FillMode::Wireframe;
- m_renderStates.m_multisampleState.m_samples = 4;
- m_renderStates.m_depthStencilState.m_depth.m_func = RHI::ComparisonFunc::Equal;
- m_renderStates.m_depthStencilState.m_stencil.m_enable = 1;
- m_renderStates.m_blendState.m_targets[0].m_blendOp = RHI::BlendOp::SubtractReverse;
- for (size_t i = 0; i < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++i)
- {
- RHI::Ptr<RHI::ShaderResourceGroupLayout> srgLayout = CreateShaderResourceGroupLayout(i);
- AZ::RHI::ShaderResourceGroupBindingInfo bindingInfo = CreateShaderResouceGroupBindingInfo(i);
- m_pipelineLayoutDescriptor->AddShaderResourceGroupLayoutInfo(*srgLayout.get(), bindingInfo);
- m_srgLayouts.push_back(srgLayout);
- }
- m_pipelineLayoutDescriptor->Finalize();
- }
- void TearDown() override
- {
- m_name = Name{};
- m_drawListName = Name{};
- for (size_t i = 0; i < m_bindings.size(); ++i)
- {
- m_bindings[i] = {};
- }
- m_srgLayouts.clear();
- m_pipelineLayoutDescriptor = nullptr;
- m_shaderOptionGroupLayoutForAsset = nullptr;
- m_shaderOptionGroupLayoutForVariants = nullptr;
- RPITestFixture::TearDown();
- }
- AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> CreateShaderOptionLayout(AZ::RHI::Handle<size_t> indexToOmit = {})
- {
- using namespace AZ;
- RPI::Ptr<RPI::ShaderOptionGroupLayout> layout = RPI::ShaderOptionGroupLayout::Create();
- for (size_t i = 0; i < m_bindings.size(); ++i)
- {
- // Allows omitting a single option to test for missing options.
- if (indexToOmit.GetIndex() != i)
- {
- layout->AddShaderOption(m_bindings[i]);
- }
- }
- layout->Finalize();
- return layout;
- }
- AZ::Name CreateShaderResourceGroupId(size_t index)
- {
- using namespace AZ;
- return Name{ AZStd::to_string(index) };
- }
- RHI::Ptr<RHI::ShaderResourceGroupLayout> CreateShaderResourceGroupLayout(size_t index)
- {
- using namespace AZ;
- Name srgId = CreateShaderResourceGroupId(index);
- // Creates a simple SRG asset with a unique SRG layout hash (based on the index).
- RHI::Ptr<RHI::ShaderResourceGroupLayout> srgLayout = RHI::ShaderResourceGroupLayout::Create();
- srgLayout->SetName(srgId);
- srgLayout->SetBindingSlot(aznumeric_caster(index));
- srgLayout->AddShaderInput(RHI::ShaderInputBufferDescriptor{
- srgId, RHI::ShaderInputBufferAccess::Read, RHI::ShaderInputBufferType::Raw, 1, 4, static_cast<uint32_t>(index), static_cast<uint32_t>(index)});
- EXPECT_TRUE(srgLayout->Finalize());
- return srgLayout;
- }
- AZ::RHI::ShaderResourceGroupBindingInfo CreateShaderResouceGroupBindingInfo(size_t index)
- {
- Name srgId = CreateShaderResourceGroupId(index);
- AZ::RHI::ShaderResourceGroupBindingInfo bindingInfo;
- bindingInfo.m_resourcesRegisterMap.insert({ srgId, RHI::ResourceBindingInfo{RHI::ShaderStageMask::Vertex, static_cast<uint32_t>(index), static_cast<uint32_t>(index)} });
- return bindingInfo;
- }
- AZ::RPI::ShaderInputContract CreateSimpleShaderInputContract()
- {
- AZ::RPI::ShaderInputContract contract;
- AZ::RPI::ShaderInputContract::StreamChannelInfo channel;
- channel.m_semantic = AZ::RHI::ShaderSemantic{ AZ::Name{"POSITION"} };
- contract.m_streamChannels.push_back(channel);
- return contract;
- }
- AZ::RPI::ShaderOutputContract CreateSimpleShaderOutputContract()
- {
- AZ::RPI::ShaderOutputContract contract;
- AZ::RPI::ShaderOutputContract::ColorAttachmentInfo attachment;
- attachment.m_componentCount = 4;
- contract.m_requiredColorAttachments.push_back(attachment);
- return contract;
- }
- RPI::ShaderVariantListSourceData::VariantInfo CreateVariantInfo(uint32_t stableId, AZStd::vector<AZStd::string> optionValues)
- {
- RPI::ShaderVariantListSourceData::VariantInfo variantInfo;
- variantInfo.m_stableId = stableId;
- auto nextValue = optionValues.begin();
- auto nextOption = m_shaderOptionGroupLayoutForVariants->GetShaderOptions().begin();
- while (nextValue != optionValues.end() &&
- nextOption != m_shaderOptionGroupLayoutForVariants->GetShaderOptions().end())
- {
- if (nextValue->empty())
- {
- // TODO (To consider) If we decide to support gaps (unqualified options) in the lookup key
- // we can actually remove this check
- variantInfo.m_options[nextOption->GetName()] = nextOption->GetDefaultValue();
- }
- else
- {
- variantInfo.m_options[nextOption->GetName()] = Name{*nextValue};
- }
- nextValue++;
- nextOption++;
- }
- return variantInfo;
- }
- // Creates and returns a shader option group with the specified option values.
- RPI::ShaderOptionGroup CreateShaderOptionGroup(AZStd::vector<Name> optionValues)
- {
- RPI::ShaderOptionGroup shaderOptionGroup(m_shaderOptionGroupLayoutForVariants);
- auto nextValue = optionValues.begin();
- auto nextOption = m_shaderOptionGroupLayoutForVariants->GetShaderOptions().begin();
- while (nextValue != optionValues.end() &&
- nextOption != m_shaderOptionGroupLayoutForVariants->GetShaderOptions().end())
- {
- if (nextValue->IsEmpty())
- {
- // TODO (To consider) If we decide to support gaps (unqualified options) in the lookup key
- // we can actually remove this check
- shaderOptionGroup.SetValue(nextOption->GetName(), nextOption->GetDefaultValue());
- }
- else
- {
- shaderOptionGroup.SetValue(nextOption->GetName(), *nextValue);
- }
- nextValue++;
- nextOption++;
- }
- return shaderOptionGroup;
- }
- Data::Asset<RPI::ShaderVariantAsset> CreateTestShaderVariantAsset(RPI::ShaderVariantId id, RPI::ShaderVariantStableId stableId,
- bool isFullyBaked,
- const AZStd::vector<RHI::ShaderStage>& stagesToActivate = {RHI::ShaderStage::Vertex, RHI::ShaderStage::Fragment})
- {
- RPI::ShaderVariantAssetCreator shaderVariantAssetCreator;
- shaderVariantAssetCreator.Begin(Uuid::CreateRandom(), id, stableId, isFullyBaked);
- for (RHI::ShaderStage rhiStage : stagesToActivate)
- {
- RHI::Ptr<RHI::ShaderStageFunction> vertexStageFunction = aznew TestShaderStageFunction(rhiStage);
- shaderVariantAssetCreator.SetShaderFunction(rhiStage, vertexStageFunction);
- }
- Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset;
- shaderVariantAssetCreator.End(shaderVariantAsset);
- return shaderVariantAsset;
- }
- void BeginCreatingTestShaderAsset(AZ::RPI::ShaderAssetCreator& creator,
- const AZStd::vector<RHI::ShaderStage>& stagesToActivate = {RHI::ShaderStage::Vertex, RHI::ShaderStage::Fragment} )
- {
- using namespace AZ;
- creator.Begin(Uuid::CreateRandom());
- creator.SetName(m_name);
- creator.SetDrawListName(m_drawListName);
- creator.SetShaderOptionGroupLayout(m_shaderOptionGroupLayoutForAsset);
- creator.BeginAPI(RHI::Factory::Get().GetType());
- creator.BeginSupervariant(AZ::Name{}); // The default (first) supervariant MUST be nameless.
- creator.SetSrgLayoutList(m_srgLayouts);
- creator.SetPipelineLayout(m_pipelineLayoutDescriptor);
- creator.SetRenderStates(m_renderStates);
- creator.SetInputContract(CreateSimpleShaderInputContract());
- creator.SetOutputContract(CreateSimpleShaderOutputContract());
- RHI::ShaderStageAttributeMapList attributeMaps;
- attributeMaps.resize(RHI::ShaderStageCount);
- creator.SetShaderStageAttributeMapList(attributeMaps);
- Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset = CreateTestShaderVariantAsset(RPI::ShaderVariantId{}, RPI::ShaderVariantStableId{0}, false, stagesToActivate);
- creator.SetRootShaderVariantAsset(shaderVariantAsset);
- creator.EndSupervariant();
- }
- //! Used to finish creating a shader that began with BeginCreatingTestShaderAsset(). Call this after adding all the desired shader variants.
- AZ::Data::Asset<AZ::RPI::ShaderAsset> EndCreatingTestShaderAsset(RPI::ShaderAssetCreator& creator)
- {
- Data::Asset<RPI::ShaderAsset> shaderAsset;
- if (creator.EndAPI())
- {
- creator.End(shaderAsset);
- }
- return shaderAsset;
- }
- AZ::Data::Asset<AZ::RPI::ShaderAsset> CreateShaderAsset()
- {
- using namespace AZ;
- RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator);
- Data::Asset<RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
- return shaderAsset;
- }
- //! The tree will only contain the root variant.
- AZ::Data::Asset<AZ::RPI::ShaderVariantTreeAsset> CreateEmptyShaderVariantTreeAsset(Data::Asset<RPI::ShaderAsset> shaderAsset)
- {
- using namespace AZ;
- AZStd::vector<RPI::ShaderVariantListSourceData::VariantInfo> shaderVariantList;
- RPI::ShaderVariantTreeAssetCreator creator;
- creator.Begin(Uuid::CreateRandom());
- creator.SetShaderOptionGroupLayout(*shaderAsset->GetShaderOptionGroupLayout());
- creator.SetVariantInfos(shaderVariantList);
- Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset;
- if (!creator.End(shaderVariantTreeAsset))
- {
- return {};
- }
- return shaderVariantTreeAsset;
- }
- AZ::Data::Asset<AZ::RPI::ShaderVariantTreeAsset> CreateShaderVariantTreeAssetForSearch(Data::Asset<RPI::ShaderAsset> shaderAsset)
- {
- using namespace AZ;
- AZStd::vector<RPI::ShaderVariantListSourceData::VariantInfo> shaderVariantList;
- shaderVariantList.push_back(CreateVariantInfo(1, { AZStd::string{"Fuchsia"} }));
- shaderVariantList.push_back(CreateVariantInfo(2, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"} }));
- shaderVariantList.push_back(CreateVariantInfo(3, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"}, AZStd::string{"50"} }));
- shaderVariantList.push_back(CreateVariantInfo(4, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"}, AZStd::string{"50"}, AZStd::string{"Off"} }));
- shaderVariantList.push_back(CreateVariantInfo(5, { AZStd::string{"Fuchsia"}, AZStd::string{"Quality::Auto"}, AZStd::string{"50"}, AZStd::string{"On"} }));
- shaderVariantList.push_back(CreateVariantInfo(6, { AZStd::string{"Teal"} }));
- shaderVariantList.push_back(CreateVariantInfo(7, { AZStd::string{"Teal"}, AZStd::string{"Quality::Sublime"} }));
- RPI::ShaderVariantTreeAssetCreator creator;
- creator.Begin(Uuid::CreateRandom()) ;
- creator.SetShaderOptionGroupLayout(*shaderAsset->GetShaderOptionGroupLayout());
- creator.SetVariantInfos(shaderVariantList);
- Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset;
- if (!creator.End(shaderVariantTreeAsset))
- {
- return {};
- }
- return shaderVariantTreeAsset;
- }
- void ValidateShaderAsset(const AZ::Data::Asset<AZ::RPI::ShaderAsset>& shaderAsset)
- {
- using namespace AZ;
- EXPECT_TRUE(shaderAsset);
- EXPECT_EQ(shaderAsset->GetName(), m_name);
- EXPECT_EQ(shaderAsset->GetDrawListName(), m_drawListName);
- EXPECT_EQ(shaderAsset->GetShaderOptionGroupLayout()->GetHash(), m_shaderOptionGroupLayoutForAsset->GetHash());
- EXPECT_EQ(shaderAsset->GetPipelineLayoutDescriptor()->GetHash(), m_pipelineLayoutDescriptor->GetHash());
- for (size_t i = 0; i < shaderAsset->GetShaderResourceGroupLayouts().size(); ++i)
- {
- auto srgLayouts = shaderAsset->GetShaderResourceGroupLayouts();
- auto& srgLayout = srgLayouts[i];
- EXPECT_EQ(srgLayout->GetHash(), m_srgLayouts[i]->GetHash());
- EXPECT_EQ(shaderAsset->FindShaderResourceGroupLayout(CreateShaderResourceGroupId(i))->GetHash(), srgLayout->GetHash());
- }
- }
- void ValidateShader(const AZ::Data::Instance<AZ::RPI::Shader>& shader)
- {
- using namespace AZ;
- EXPECT_TRUE(shader);
- EXPECT_TRUE(shader->GetAsset());
- auto shaderAsset = shader->GetAsset();
- EXPECT_EQ(shader->GetPipelineStateType(), shaderAsset->GetPipelineStateType());
- using ShaderResourceGroupLayoutSpan = AZStd::span<const AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout>>;
- ShaderResourceGroupLayoutSpan shaderResourceGroupLayoutSpan = shader->GetShaderResourceGroupLayouts();
- ShaderResourceGroupLayoutSpan shaderAssetResourceGroupLayoutSpan = shader->GetShaderResourceGroupLayouts();
- EXPECT_EQ(shaderResourceGroupLayoutSpan.data(), shaderAssetResourceGroupLayoutSpan.data());
- EXPECT_EQ(shaderResourceGroupLayoutSpan.size(), shaderAssetResourceGroupLayoutSpan.size());
- const RPI::ShaderVariant& rootShaderVariant = shader->GetVariant( RPI::ShaderVariantStableId{0} );
- RHI::PipelineStateDescriptorForDraw descriptorForDraw;
- rootShaderVariant.ConfigurePipelineState(descriptorForDraw);
- EXPECT_EQ(descriptorForDraw.m_pipelineLayoutDescriptor->GetHash(), m_pipelineLayoutDescriptor->GetHash());
- EXPECT_NE(descriptorForDraw.m_vertexFunction, nullptr);
- EXPECT_NE(descriptorForDraw.m_fragmentFunction, nullptr);
- EXPECT_EQ(descriptorForDraw.m_renderStates.GetHash(), m_renderStates.GetHash());
- EXPECT_EQ(descriptorForDraw.m_inputStreamLayout.GetHash(), HashValue64{ 0 }); // ConfigurePipelineState shouldn't touch descriptorForDraw.m_inputStreamLayout
- EXPECT_EQ(descriptorForDraw.m_renderAttachmentConfiguration.GetHash(), RHI::RenderAttachmentConfiguration().GetHash()); // ConfigurePipelineState shouldn't touch descriptorForDraw.m_outputAttachmentLayout
- // Actual layout content doesn't matter for this test, it just needs to be set up to pass validation inside AcquirePipelineState().
- descriptorForDraw.m_inputStreamLayout.SetTopology(RHI::PrimitiveTopology::TriangleList);
- descriptorForDraw.m_inputStreamLayout.Finalize();
- RHI::RenderAttachmentLayoutBuilder builder;
- builder.AddSubpass()
- ->RenderTargetAttachment(RHI::Format::R8G8B8A8_SNORM)
- ->DepthStencilAttachment(RHI::Format::R32_FLOAT);
- builder.End(descriptorForDraw.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
- const RHI::PipelineState* pipelineState = shader->AcquirePipelineState(descriptorForDraw);
- EXPECT_NE(pipelineState, nullptr);
- }
- AZStd::array<AZ::RPI::ShaderOptionDescriptor, 4> m_bindings;
- AZ::Name m_name;
- AZ::Name m_drawListName;
- AZ::RHI::Ptr<AZ::RHI::PipelineLayoutDescriptor> m_pipelineLayoutDescriptor;
- AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> m_shaderOptionGroupLayoutForAsset;
- AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> m_shaderOptionGroupLayoutForVariants;
- AZ::RHI::RenderStates m_renderStates;
- AZStd::fixed_vector<AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout>, AZ::RHI::Limits::Pipeline::ShaderResourceGroupCountMax> m_srgLayouts;
- };
- TEST_F(ShaderTests, ShaderOptionBindingTest)
- {
- using namespace AZ;
- EXPECT_EQ(m_bindings[0].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(4, 0) });
- EXPECT_EQ(m_bindings[1].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(3, 4) });
- EXPECT_EQ(m_bindings[2].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(8, 7) });
- EXPECT_EQ(m_bindings[3].GetBitMask(), RPI::ShaderVariantKey{ AZ_BIT_MASK_OFFSET(1, 15) });
- EXPECT_TRUE(m_bindings[0].FindValue(Name("Navy")).IsValid());
- EXPECT_FALSE(m_bindings[0].FindValue(Name("Color::Navy")).IsValid()); // Not found - Color is unscoped
- EXPECT_TRUE(m_bindings[1].FindValue(Name("Quality::Average")).IsValid());
- EXPECT_FALSE(m_bindings[1].FindValue(Name("Average")).IsValid()); // Not found - Quality is scoped
- EXPECT_FALSE(m_bindings[1].FindValue(Name("Cake")).IsValid()); // Not found - Cake is not on the list
- EXPECT_FALSE(m_bindings[1].FindValue(Name("Quality::Cake")).IsValid()); // Not found - still not on the list
- EXPECT_TRUE(m_bindings[2].FindValue(Name("5")).IsValid());
- EXPECT_TRUE(m_bindings[2].FindValue(Name("200")).IsValid());
- EXPECT_TRUE(m_bindings[2].FindValue(Name("42")).IsValid());
- EXPECT_FALSE(m_bindings[2].FindValue(Name("-1")).IsValid()); // Not found - less than MinValue
- EXPECT_FALSE(m_bindings[2].FindValue(Name("1001")).IsValid()); // Not found - more than MaxValue
- EXPECT_TRUE(m_bindings[3].FindValue(Name("Off")).IsValid());
- EXPECT_TRUE(m_bindings[3].FindValue(Name("On")).IsValid());
- EXPECT_FALSE(m_bindings[3].FindValue(Name("False")).IsValid()); // Not found - the correct user-defined id is Off
- EXPECT_FALSE(m_bindings[3].FindValue(Name("True")).IsValid()); // Not found - the correct user-defined id is On
- EXPECT_EQ(m_bindings[0].GetValueName(RPI::ShaderOptionValue(4)), Name("Navy"));
- EXPECT_EQ(m_bindings[1].GetValueName(RPI::ShaderOptionValue(3)), Name("Quality::Average"));
- EXPECT_EQ(m_bindings[2].GetValueName(RPI::ShaderOptionValue(200)), Name("200"));
- EXPECT_EQ(m_bindings[3].GetValueName(RPI::ShaderOptionValue(0)), Name("Off"));
- EXPECT_EQ(m_bindings[3].GetValueName(RPI::ShaderOptionValue(1)), Name("On"));
- EXPECT_TRUE(m_bindings[2].GetValueName(RPI::ShaderOptionValue(-1)).IsEmpty()); // No matching value
- EXPECT_TRUE(m_bindings[2].GetValueName(RPI::ShaderOptionValue(1001)).IsEmpty()); // No matching value
- RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
- bool success = shaderOptionGroupLayout->AddShaderOption(m_bindings[0]);
- EXPECT_TRUE(success);
- success = shaderOptionGroupLayout->AddShaderOption(m_bindings[1]);
- EXPECT_TRUE(success);
- success = shaderOptionGroupLayout->AddShaderOption(m_bindings[2]);
- EXPECT_TRUE(success);
- success = shaderOptionGroupLayout->AddShaderOption(m_bindings[3]);
- EXPECT_TRUE(success);
- shaderOptionGroupLayout->Finalize();
- EXPECT_TRUE(shaderOptionGroupLayout->IsFinalized());
- RPI::ShaderOptionGroup testGroup(shaderOptionGroupLayout);
- m_bindings[0].Set(testGroup, m_bindings[0].FindValue(Name("Gray")));
- EXPECT_EQ(m_bindings[0].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(8).GetIndex());
- m_bindings[0].Set(testGroup, RPI::ShaderOptionValue(1));
- EXPECT_EQ(m_bindings[0].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(1).GetIndex());
- testGroup.SetValue(Name("Color"), Name("Olive"));
- EXPECT_EQ(testGroup.GetValue(Name("Color")).GetIndex(), RPI::ShaderOptionValue(3).GetIndex());
- testGroup.SetValue(Name("Color"), RPI::ShaderOptionValue(5));
- EXPECT_EQ(testGroup.GetValue(Name("Color")).GetIndex(), RPI::ShaderOptionValue(5).GetIndex());
- testGroup.SetValue(RPI::ShaderOptionIndex(0), Name("Lime"));
- EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(0)).GetIndex(), RPI::ShaderOptionValue(10).GetIndex());
- testGroup.SetValue(RPI::ShaderOptionIndex(0), RPI::ShaderOptionValue(0));
- EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(0)).GetIndex(), RPI::ShaderOptionValue(0).GetIndex());
- m_bindings[1].Set(testGroup, m_bindings[1].FindValue(Name("Quality::Average")));
- EXPECT_EQ(m_bindings[1].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(3).GetIndex());
- m_bindings[1].Set(testGroup, RPI::ShaderOptionValue(1));
- EXPECT_EQ(m_bindings[1].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(1).GetIndex());
- testGroup.SetValue(Name("Quality"), Name("Quality::Ultra"));
- EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(6).GetIndex());
- testGroup.SetValue(Name("Quality"), RPI::ShaderOptionValue(5));
- EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(5).GetIndex());
- testGroup.SetValue(RPI::ShaderOptionIndex(1), Name("Quality::Auto"));
- EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(1)).GetIndex(), RPI::ShaderOptionValue(0).GetIndex());
- testGroup.SetValue(RPI::ShaderOptionIndex(1), RPI::ShaderOptionValue(2));
- EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(1)).GetIndex(), RPI::ShaderOptionValue(2).GetIndex());
- m_bindings[2].Set(testGroup, m_bindings[2].FindValue(Name("150")));
- EXPECT_EQ(m_bindings[2].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(150).GetIndex());
- m_bindings[2].Set(testGroup, RPI::ShaderOptionValue(120));
- EXPECT_EQ(m_bindings[2].Get(testGroup).GetIndex(), RPI::ShaderOptionValue(120).GetIndex());
- testGroup.SetValue(Name("NumberSamples"), Name("101"));
- EXPECT_EQ(testGroup.GetValue(Name("NumberSamples")).GetIndex(), RPI::ShaderOptionValue(101).GetIndex());
- testGroup.SetValue(Name("NumberSamples"), RPI::ShaderOptionValue(102));
- EXPECT_EQ(testGroup.GetValue(Name("NumberSamples")).GetIndex(), RPI::ShaderOptionValue(102).GetIndex());
- testGroup.SetValue(RPI::ShaderOptionIndex(2), Name("103"));
- EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(2)).GetIndex(), RPI::ShaderOptionValue(103).GetIndex());
- testGroup.SetValue(RPI::ShaderOptionIndex(2), RPI::ShaderOptionValue(104));
- EXPECT_EQ(testGroup.GetValue(RPI::ShaderOptionIndex(2)).GetIndex(), RPI::ShaderOptionValue(104).GetIndex());
- // Tests for invalid or Null value id
- // Setting a valid value id changes the key
- testGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
- EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(7).GetIndex());
- // "Cake" is delicious, but it's not a valid option for "Quality"
- // Setting an invalid value id does nothing - it's ignored, so the key remains the same
- AZ_TEST_START_TRACE_SUPPRESSION;
- testGroup.SetValue(Name("Quality"), Name("Cake"));
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- EXPECT_EQ(testGroup.GetValue(Name("Quality")).GetIndex(), RPI::ShaderOptionValue(7).GetIndex());
- // ClearValue clears the mask
- testGroup.ClearValue(Name("Quality"));
- EXPECT_EQ(testGroup.GetValue(Name("Quality")).IsNull(), true);
- }
- TEST_F(ShaderTests, ShaderOptionGroupLayoutTest)
- {
- using namespace AZ;
- RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
- bool success = shaderOptionGroupLayout->AddShaderOption(m_bindings[0]);
- EXPECT_TRUE(success);
- success = shaderOptionGroupLayout->AddShaderOption(m_bindings[1]);
- EXPECT_TRUE(success);
- success = shaderOptionGroupLayout->AddShaderOption(m_bindings[2]);
- EXPECT_TRUE(success);
- success = shaderOptionGroupLayout->AddShaderOption(m_bindings[3]);
- EXPECT_TRUE(success);
- auto intRangeType = RPI::ShaderOptionType::IntegerRange;
- uint32_t order = m_bindings[3].GetOrder() + 1; // The tests below will fail anyway, but still
- ErrorMessageFinder errorMessageFinder;
- // Overlaps previous mask.
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("mask overlaps with previously added masks");
- AZStd::vector<RPI::ShaderOptionValuePair> list0;
- list0.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
- list0.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 6, order++, list0, Name("0") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Add shader option that extends past end of bit mask.
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("exceeds size of mask");
- AZStd::vector<RPI::ShaderOptionValuePair> list1;
- list1.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
- list1.push_back({ Name("255"), RPI::ShaderOptionValue(255) }); // 8+ bit
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, RPI::ShaderVariantKeyBitCount - 4, order++, list1, Name("0") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Add shader option with empty name.
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("empty name");
- AZStd::vector<RPI::ShaderOptionValuePair> list2;
- list2.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
- list2.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{}, intRangeType, 16, order++, list2, Name("0") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Add shader option with empty bits.
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("has zero bits");
- AZStd::vector<RPI::ShaderOptionValuePair> list3;
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 16, order++, list3, Name("0") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // An integer range option must have at least two values defining the range
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("has zero bits");
- AZStd::vector<RPI::ShaderOptionValuePair> list3b;
- list3b.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 16, order++, list3b, Name("0") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Add a shader option with an order that collides with an existing shader option
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("has the same order");
- uint32_t bitOffset = m_bindings[3].GetBitOffset() + m_bindings[3].GetBitCount();
- AZStd::vector<RPI::ShaderOptionValuePair> list4;
- list4.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
- list4.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, bitOffset, 0, list4, Name("0") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Add shader option with an invalid default int value.
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("invalid default value");
- AZStd::vector<RPI::ShaderOptionValuePair> list6;
- list6.push_back({ Name("0"), RPI::ShaderOptionValue(0) }); // 1+ bit
- list6.push_back({ Name("1"), RPI::ShaderOptionValue(1) }); // ...
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, intRangeType, 16, order++, list6, Name("3") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Add shader option with an invalid default enum value.
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("invalid default value");
- AZStd::vector<RPI::ShaderOptionValuePair> list7;
- list7.push_back({ Name("TypeA"), RPI::ShaderOptionValue(0) });
- list7.push_back({ Name("TypeB"), RPI::ShaderOptionValue(1) });
- list7.push_back({ Name("TypeC"), RPI::ShaderOptionValue(2) });
- success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"Invalid"}, RPI::ShaderOptionType::Enumeration, 16, order++, list7, Name("TypeO") });
- EXPECT_FALSE(success);
- errorMessageFinder.CheckExpectedErrorsFound();
- // Test access before finalize.
- EXPECT_FALSE(shaderOptionGroupLayout->IsFinalized());
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("ShaderOptionGroupLayout is not finalized", 4);
- EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[0].GetName()), RPI::ShaderOptionIndex());
- EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[1].GetName()), RPI::ShaderOptionIndex());
- EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[2].GetName()), RPI::ShaderOptionIndex());
- EXPECT_EQ(shaderOptionGroupLayout->FindShaderOptionIndex(m_bindings[3].GetName()), RPI::ShaderOptionIndex());
- errorMessageFinder.CheckExpectedErrorsFound();
- {
- errorMessageFinder.Reset();
- errorMessageFinder.AddExpectedErrorMessage("ShaderOptionGroupLayout is not finalized");
- RPI::ShaderVariantKey testKey = 1;
- EXPECT_FALSE(shaderOptionGroupLayout->IsValidShaderVariantKey(testKey));
- errorMessageFinder.CheckExpectedErrorsFound();
- }
- errorMessageFinder.Disable();
- shaderOptionGroupLayout->Finalize();
- EXPECT_TRUE(shaderOptionGroupLayout->IsFinalized());
- EXPECT_EQ(shaderOptionGroupLayout->GetShaderOptionCount(), 4);
- EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(0)), m_bindings[0]);
- EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(1)), m_bindings[1]);
- EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(2)), m_bindings[2]);
- EXPECT_EQ(shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex(3)), m_bindings[3]);
- RPI::ShaderVariantKey unionMask;
- for (const auto& binding : m_bindings)
- {
- unionMask |= binding.GetBitMask();
- }
- EXPECT_EQ(unionMask, shaderOptionGroupLayout->GetBitMask());
- EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[0].GetBitMask()));
- EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[1].GetBitMask()));
- EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[2].GetBitMask()));
- EXPECT_TRUE(shaderOptionGroupLayout->IsValidShaderVariantKey(m_bindings[3].GetBitMask()));
- // Test value-lookup functions
- auto& colorOption = shaderOptionGroupLayout->GetShaderOption(RPI::ShaderOptionIndex{ 0 });
- EXPECT_EQ(colorOption.FindValue(Name{ "Navy" }).GetIndex(), 4);
- EXPECT_EQ(colorOption.FindValue(Name{ "Purple" }).GetIndex(), 5);
- EXPECT_FALSE(colorOption.FindValue(Name{ "Blah" }).IsValid());
- EXPECT_EQ(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 0 }, Name{ "Navy" }).GetIndex(), 4);
- EXPECT_EQ(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 0 }, Name{ "Purple" }).GetIndex(), 5);
- EXPECT_EQ(shaderOptionGroupLayout->FindValue(Name{ "Color" }, Name{ "Navy" }).GetIndex(), 4);
- EXPECT_EQ(shaderOptionGroupLayout->FindValue(Name{ "Color" }, Name{ "Purple" }).GetIndex(), 5);
- EXPECT_FALSE(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 0 }, Name{ "Blah" }).IsValid());
- EXPECT_FALSE(shaderOptionGroupLayout->FindValue(Name{ "Color" }, Name{ "Blah" }).IsValid());
- EXPECT_FALSE(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{}, Name{ "Navy" }).IsValid());
- EXPECT_FALSE(shaderOptionGroupLayout->FindValue(RPI::ShaderOptionIndex{ 100 }, Name{ "Navy" }).IsValid());
- EXPECT_FALSE(shaderOptionGroupLayout->FindValue(Name{ "Blah" }, Name{ "Navy" }).IsValid());
- EXPECT_FALSE(shaderOptionGroupLayout->FindShaderOptionIndex(Name{ "Invalid" }).IsValid());
- }
- TEST_F(ShaderTests, ImplicitDefaultValue)
- {
- // Add shader option with no default value.
- RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
- AZStd::vector<RPI::ShaderOptionValuePair> values = AZ::RPI::CreateEnumShaderOptionValues({"A", "B", "C"});
- bool success = shaderOptionGroupLayout->AddShaderOption(AZ::RPI::ShaderOptionDescriptor{ Name{"NoDefaultSpecified"}, RPI::ShaderOptionType::Enumeration, 0, 0, values });
- EXPECT_TRUE(success);
- EXPECT_STREQ("A", shaderOptionGroupLayout->GetShaderOptions().back().GetDefaultValue().GetCStr());
- }
- TEST_F(ShaderTests, ShaderOptionGroupTest)
- {
- using namespace AZ;
- RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
- EXPECT_TRUE(group.GetShaderVariantId().IsEmpty());
- group.SetValue(RPI::ShaderOptionIndex(0), RPI::ShaderOptionValue(7));
- group.SetValue(RPI::ShaderOptionIndex(1), RPI::ShaderOptionValue(6));
- group.SetValue(RPI::ShaderOptionIndex(2), RPI::ShaderOptionValue(5));
- group.SetValue(RPI::ShaderOptionIndex(3), RPI::ShaderOptionValue(1));
- group.SetValue(group.FindShaderOptionIndex(m_bindings[0].GetName()), RPI::ShaderOptionValue(7));
- group.SetValue(group.FindShaderOptionIndex(m_bindings[1].GetName()), RPI::ShaderOptionValue(6));
- group.SetValue(group.FindShaderOptionIndex(m_bindings[2].GetName()), RPI::ShaderOptionValue(5));
- group.SetValue(group.FindShaderOptionIndex(m_bindings[3].GetName()), RPI::ShaderOptionValue(1));
- EXPECT_FALSE(group.GetShaderVariantId().IsEmpty());
- EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[0].GetName())).GetIndex(), 7);
- EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[1].GetName())).GetIndex(), 6);
- EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[2].GetName())).GetIndex(), 5);
- EXPECT_EQ(group.GetValue(group.FindShaderOptionIndex(m_bindings[3].GetName())).GetIndex(), 1);
- EXPECT_EQ(group.FindShaderOptionIndex(Name{}), RPI::ShaderOptionIndex{});
- EXPECT_EQ(group.FindShaderOptionIndex(Name{ "Invalid" }), RPI::ShaderOptionIndex{});
- // Helper methods - these are suboptimal since because they fetch index from id
- // The intended use for these methods is in prototypes and simple sample code
- group.SetValue(Name("Color"), Name("Fuchsia")); // 13
- group.SetValue(Name("Quality"), Name("Quality::Sublime")); // 7
- group.SetValue(Name("NumberSamples"), Name("190")); // 190
- group.SetValue(Name("Raytracing"), Name("On")); // 1
- EXPECT_EQ(group.GetValue(Name("Color")).GetIndex(), 13);
- EXPECT_EQ(group.GetValue(Name("Quality")).GetIndex(), 7);
- EXPECT_EQ(group.GetValue(Name("NumberSamples")).GetIndex(), 190);
- EXPECT_EQ(group.GetValue(Name("Raytracing")).GetIndex(), 1);
- }
- RPI::Ptr<RPI::ShaderOptionGroupLayout> CreateOptionsLayoutWithAllBools()
- {
- AZStd::vector<RPI::ShaderOptionValuePair> boolIdList;
- boolIdList.push_back({Name("Off"), RPI::ShaderOptionValue(0)});
- boolIdList.push_back({Name("On"), RPI::ShaderOptionValue(1)});
- RPI::Ptr<RPI::ShaderOptionGroupLayout> layout = RPI::ShaderOptionGroupLayout::Create();
- for (uint32_t i = 0; i < AZ::RPI::ShaderVariantKeyBitCount; ++i)
- {
- RPI::ShaderOptionDescriptor option{
- Name{AZStd::string::format("option%d", i)},
- RPI::ShaderOptionType::Boolean,
- i,
- i,
- boolIdList,
- Name{"Off"}};
- EXPECT_TRUE(layout->AddShaderOption(option));
- }
- layout->Finalize();
- return layout;
- }
- TEST_F(ShaderTests, ShaderOptionGroup_AccessEachBit_AllOtherOptionsUnspecified)
- {
- AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> allBitsOff;
- for (size_t i = 0; i < AZ::RPI::ShaderVariantKeyBitCount; ++i)
- {
- // Verify the assumption that bitset is initialized to all false
- EXPECT_FALSE(allBitsOff[i]);
- }
- for (size_t targetBit = 0; targetBit < AZ::RPI::ShaderVariantKeyBitCount; ++targetBit)
- {
- RPI::ShaderOptionGroup group(CreateOptionsLayoutWithAllBools());
- // Set target bit on, all other bits are unspecified
- group.SetValue(AZ::RPI::ShaderOptionIndex{targetBit}, AZ::RPI::ShaderOptionValue{1});
- for (int j = 0; j < AZ::RPI::ShaderVariantKeyBitCount; ++j)
- {
- if (j == targetBit)
- {
- EXPECT_TRUE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
- EXPECT_EQ(1, group.GetValue(AZ::RPI::ShaderOptionIndex{j}).GetIndex());
- }
- else
- {
- EXPECT_FALSE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
- }
- }
- AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> expected = allBitsOff;
- expected[targetBit] = true;
- EXPECT_EQ(expected, group.GetShaderVariantId().m_key);
- EXPECT_EQ(expected, group.GetShaderVariantId().m_mask);
- }
- }
- TEST_F(ShaderTests, ShaderOptionGroup_AccessEachBit_AllOtherOptionsTrue)
- {
- AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> allBitsOn;
- allBitsOn.set();
- for (size_t i = 0; i < AZ::RPI::ShaderVariantKeyBitCount; ++i)
- {
- EXPECT_TRUE(allBitsOn[i]);
- }
- for (size_t targetBit = 0; targetBit < AZ::RPI::ShaderVariantKeyBitCount; ++targetBit)
- {
- RPI::ShaderOptionGroup group(CreateOptionsLayoutWithAllBools());
- // Set all other bits on
- for (int j = 0; j < AZ::RPI::ShaderVariantKeyBitCount; ++j)
- {
- group.SetValue(AZ::RPI::ShaderOptionIndex{j}, AZ::RPI::ShaderOptionValue{1});
- }
- // Set the target bit off
- group.SetValue(AZ::RPI::ShaderOptionIndex{targetBit}, AZ::RPI::ShaderOptionValue{0});
- for (int j = 0; j < AZ::RPI::ShaderVariantKeyBitCount; ++j)
- {
- if (j == targetBit)
- {
- EXPECT_TRUE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
- EXPECT_EQ(0, group.GetValue(AZ::RPI::ShaderOptionIndex{j}).GetIndex());
- }
- else
- {
- EXPECT_TRUE(group.GetValue(AZ::RPI::ShaderOptionIndex{j}).IsValid());
- EXPECT_EQ(1, group.GetValue(AZ::RPI::ShaderOptionIndex{j}).GetIndex());
- }
- }
- AZStd::bitset<AZ::RPI::ShaderVariantKeyBitCount> expected = allBitsOn;
- expected[targetBit] = false;
- EXPECT_EQ(expected, group.GetShaderVariantId().m_key);
- EXPECT_EQ(allBitsOn, group.GetShaderVariantId().m_mask);
- }
- }
- TEST_F(ShaderTests, ShaderOptionGroup_SetAllToDefaultValues)
- {
- using namespace AZ;
- RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
- EXPECT_FALSE(group.GetValue(Name{"Color"}).IsValid());
- EXPECT_FALSE(group.GetValue(Name{"Quality"}).IsValid());
- EXPECT_FALSE(group.GetValue(Name{"NumberSamples"}).IsValid());
- EXPECT_FALSE(group.GetValue(Name{"Raytracing"}).IsValid());
- group.SetAllToDefaultValues();
- EXPECT_EQ(13, group.GetValue(Name{"Color"}).GetIndex());
- EXPECT_EQ(0, group.GetValue(Name{"Quality"}).GetIndex());
- EXPECT_EQ(50, group.GetValue(Name{"NumberSamples"}).GetIndex());
- EXPECT_EQ(0, group.GetValue(Name{"Raytracing"}).GetIndex());
- }
- TEST_F(ShaderTests, ShaderOptionGroup_SetUnspecifiedToDefaultValues)
- {
- using namespace AZ;
- RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
- EXPECT_FALSE(group.GetValue(Name{"Color"}).IsValid());
- EXPECT_FALSE(group.GetValue(Name{"Quality"}).IsValid());
- EXPECT_FALSE(group.GetValue(Name{"NumberSamples"}).IsValid());
- EXPECT_FALSE(group.GetValue(Name{"Raytracing"}).IsValid());
- group.SetValue(Name{"Color"}, Name("Yellow"));
- group.SetValue(Name{"Raytracing"}, Name("On"));
- group.SetUnspecifiedToDefaultValues();
- EXPECT_EQ(11, group.GetValue(Name{"Color"}).GetIndex());
- EXPECT_EQ(0, group.GetValue(Name{"Quality"}).GetIndex());
- EXPECT_EQ(50, group.GetValue(Name{"NumberSamples"}).GetIndex());
- EXPECT_EQ(1, group.GetValue(Name{"Raytracing"}).GetIndex());
- }
- TEST_F(ShaderTests, ShaderOptionGroup_ToString)
- {
- using namespace AZ;
- RPI::ShaderOptionGroup group(m_shaderOptionGroupLayoutForAsset);
- group.SetValue(Name("Color"), Name("Silver")); // 7
- group.SetValue(Name("NumberSamples"), Name("50")); // 50
- group.SetValue(Name("Raytracing"), Name("On")); // 1
- EXPECT_STREQ("Color=7, Quality=?, NumberSamples=50, Raytracing=1", group.ToString().c_str());
- }
- TEST_F(ShaderTests, ShaderOptionGroupTest_Errors)
- {
- using namespace AZ::RPI;
- Ptr<ShaderOptionGroupLayout> layout = CreateShaderOptionLayout();
- ShaderOptionIndex colorIndex = layout->FindShaderOptionIndex(Name{ "Color" });
- ShaderOptionValue redValue = layout->GetShaderOption(colorIndex).FindValue(Name("Red"));
- RPI::ShaderOptionGroup group(layout);
- // Setting by option index and value index...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, ShaderOptionValue{}));
- EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, redValue));
- EXPECT_FALSE(group.SetValue(colorIndex, ShaderOptionValue{}));
- AZ_TEST_STOP_TRACE_SUPPRESSION(3);
- EXPECT_TRUE(group.SetValue(colorIndex, redValue));
- // Setting by option name and value index...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, ShaderOptionValue{}));
- EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, redValue));
- EXPECT_FALSE(group.SetValue(Name{ "Color" }, ShaderOptionValue{}));
- AZ_TEST_STOP_TRACE_SUPPRESSION(3);
- EXPECT_TRUE(group.SetValue(Name{ "Color" }, redValue));
- // Setting by option index and value name...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, Name{ "DoesNotExist" }));
- EXPECT_FALSE(group.SetValue(ShaderOptionIndex{}, Name{ "Red" }));
- EXPECT_FALSE(group.SetValue(colorIndex, Name{ "DoesNotExist" }));
- AZ_TEST_STOP_TRACE_SUPPRESSION(3);
- EXPECT_TRUE(group.SetValue(colorIndex, Name{ "Red" }));
- // Setting by option name and value name...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, Name{ "DoesNotExist" }));
- EXPECT_FALSE(group.SetValue(Name{ "DoesNotExist" }, Name{ "Red" }));
- EXPECT_FALSE(group.SetValue(Name{ "Color" }, Name{ "DoesNotExist" }));
- AZ_TEST_STOP_TRACE_SUPPRESSION(3);
- EXPECT_TRUE(group.SetValue(Name{ "Color" }, Name{ "Red" }));
- // GetValue by option index...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.GetValue(ShaderOptionIndex{}).IsValid());
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- EXPECT_TRUE(group.GetValue(colorIndex).IsValid());
- // GetValue by option name...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.GetValue(Name{ "DoesNotExist" }).IsValid());
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- EXPECT_TRUE(group.GetValue(Name{ "Color" }).IsValid());
- // Clearing by option index...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.ClearValue(ShaderOptionIndex{}));
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- EXPECT_TRUE(group.ClearValue(colorIndex));
- // Clearing by option name...
- AZ_TEST_START_TRACE_SUPPRESSION;
- EXPECT_FALSE(group.ClearValue(Name{ "DoesNotExist" }));
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- EXPECT_TRUE(group.ClearValue(Name{ "Color" }));
- }
- TEST_F(ShaderTests, ShaderAsset_Baseline_Test)
- {
- using namespace AZ;
- ValidateShaderAsset(CreateShaderAsset());
- }
- TEST_F(ShaderTests, ShaderAsset_PipelineStateType_VertexImpliesDraw)
- {
- AZ::RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator, {RHI::ShaderStage::Vertex});
- AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
- EXPECT_TRUE(shaderAsset);
- EXPECT_EQ(shaderAsset->GetPipelineStateType(), RHI::PipelineStateType::Draw);
- }
- TEST_F(ShaderTests, ShaderAsset_PipelineStateType_ComputeImpliesDispatch)
- {
- AZ::RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator, {AZ::RHI::ShaderStage::Compute});
- AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
- EXPECT_TRUE(shaderAsset);
- EXPECT_EQ(shaderAsset->GetPipelineStateType(), RHI::PipelineStateType::Dispatch);
- }
- TEST_F(ShaderTests, ShaderAsset_PipelineStateType_Error_DrawAndDispatch)
- {
- ErrorMessageFinder messageFinder("both Draw functions and Dispatch functions");
- messageFinder.AddExpectedErrorMessage("Invalid root variant");
- messageFinder.AddExpectedErrorMessage("Cannot continue building ShaderAsset because 1 error(s) reported");
- AZ::RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator,
- {AZ::RHI::ShaderStage::Vertex, AZ::RHI::ShaderStage::Fragment, AZ::RHI::ShaderStage::Compute});
- AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
- EXPECT_FALSE(shaderAsset);
- }
- TEST_F(ShaderTests, ShaderAsset_Error_FragmentFunctionRequiresVertexFunction)
- {
- ErrorMessageFinder messageFinder("fragment function but no vertex function");
- messageFinder.AddExpectedErrorMessage("Invalid root variant");
- messageFinder.AddExpectedErrorMessage("Cannot continue building ShaderAsset because 1 error(s) reported");
- AZ::RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator, {AZ::RHI::ShaderStage::Fragment});
- AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
- messageFinder.CheckExpectedErrorsFound();
- EXPECT_FALSE(shaderAsset);
- }
- TEST_F(ShaderTests, ShaderAsset_Error_GeometryFunctionRequiresVertexFunction)
- {
- ErrorMessageFinder messageFinder("geometry function but no vertex function");
- messageFinder.AddExpectedErrorMessage("Invalid root variant");
- messageFinder.AddExpectedErrorMessage("Cannot continue building ShaderAsset because 1 error(s) reported");
- AZ::RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator, { AZ::RHI::ShaderStage::Geometry });
- AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = EndCreatingTestShaderAsset(creator);
- messageFinder.CheckExpectedErrorsFound();
- EXPECT_FALSE(shaderAsset);
- }
- TEST_F(ShaderTests, ShaderAsset_Serialize_Test)
- {
- using namespace AZ;
- Data::Asset<RPI::ShaderAsset> shaderAsset = CreateShaderAsset();
- ValidateShaderAsset(shaderAsset);
- RPI::ShaderAssetTester tester(GetSerializeContext());
- tester.SerializeOut(shaderAsset.Get());
- Data::Asset<RPI::ShaderAsset> serializedShaderAsset = tester.SerializeInHelper(Uuid::CreateRandom());
- ValidateShaderAsset(serializedShaderAsset);
- }
- TEST_F(ShaderTests, ShaderAsset_PipelineLayout_Missing_Test)
- {
- using namespace AZ;
- m_pipelineLayoutDescriptor = nullptr;
- AZ_TEST_START_TRACE_SUPPRESSION;
- Data::Asset<RPI::ShaderAsset> shaderAsset = CreateShaderAsset();
- AZ_TEST_STOP_TRACE_SUPPRESSION(2);
- EXPECT_FALSE(shaderAsset);
- }
- TEST_F(ShaderTests, ShaderAsset_ShaderOptionGroupLayout_Mismatch_Test)
- {
- using namespace AZ;
- const size_t indexToOmit = 0;
- // Creates a shader option group layout assigned to the asset which doesn't match the
- // one assigned to the the variants.
- m_shaderOptionGroupLayoutForAsset = CreateShaderOptionLayout(RHI::Handle<size_t>(indexToOmit));
- AZ_TEST_START_TRACE_SUPPRESSION;
- Data::Asset<RPI::ShaderAsset> shaderAsset = CreateShaderAsset();
- Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset = CreateShaderVariantTreeAssetForSearch(shaderAsset);
- AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
- EXPECT_FALSE(shaderVariantTreeAsset);
- }
- TEST_F(ShaderTests, ShaderAsset_DefaultShaderOptions)
- {
- using namespace AZ;
- RPI::ShaderAssetCreator creator;
- BeginCreatingTestShaderAsset(creator);
- // Override two of the default values. The others will maintain the default value from the shader options layout, see SetUp().
- creator.SetShaderOptionDefaultValue(Name{"Quality"}, Name{"Quality::Average"});
- creator.SetShaderOptionDefaultValue(Name{"Raytracing"}, Name{"On"});
- Data::Asset<RPI::ShaderAsset> shaderAssetWithShaderOptionOverrides = EndCreatingTestShaderAsset(creator);
- // These options were overridden
- EXPECT_EQ(3, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"Quality"}).GetIndex());
- EXPECT_EQ(1, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"Raytracing"}).GetIndex());
- // These options maintain their original default values
- EXPECT_EQ(13, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"Color"}).GetIndex());
- EXPECT_EQ(50, shaderAssetWithShaderOptionOverrides->GetDefaultShaderOptions().GetValue(Name{"NumberSamples"}).GetIndex());
- }
- TEST_F(ShaderTests, Shader_Baseline_Test)
- {
- using namespace AZ;
- Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(CreateShaderAsset());
- ValidateShader(shader);
- }
- TEST_F(ShaderTests, ValidateShaderVariantIdMath)
- {
- RPI::ShaderVariantId idSmall;
- RPI::ShaderVariantId idLarge;
- RPI::ShaderVariantIdComparator idComparator;
- idSmall.m_mask = RPI::ShaderVariantKey(15);
- idLarge.m_mask = RPI::ShaderVariantKey(31);
- idSmall.m_key = RPI::ShaderVariantKey(15);
- idLarge.m_key = RPI::ShaderVariantKey(31);
- EXPECT_TRUE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
- // The mask has precedence so the evaluation is the same as above
- idSmall.m_key = RPI::ShaderVariantKey(31);
- idLarge.m_key = RPI::ShaderVariantKey(15);
- EXPECT_TRUE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
- // The mask has precedence so the evaluation is the same as above
- idSmall.m_key = RPI::ShaderVariantKey(0);
- idLarge.m_key = RPI::ShaderVariantKey(0);
- EXPECT_TRUE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
- // The mask has precedence so the evaluation is the same as above
- idSmall.m_key = RPI::ShaderVariantKey(63);
- idLarge.m_key = RPI::ShaderVariantKey(63);
- EXPECT_TRUE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
- // In the case where the mask are equal, the id's key should be used
- idSmall.m_mask = RPI::ShaderVariantKey(31);
- idLarge.m_mask = RPI::ShaderVariantKey(31);
- idSmall.m_key = RPI::ShaderVariantKey(6);
- idLarge.m_key = RPI::ShaderVariantKey(20);
- EXPECT_TRUE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), -1);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 1);
- // The variant id is the same
- idSmall.m_mask = RPI::ShaderVariantKey(31);
- idLarge.m_mask = RPI::ShaderVariantKey(31);
- idSmall.m_key = RPI::ShaderVariantKey(15);
- idLarge.m_key = RPI::ShaderVariantKey(15);
- EXPECT_FALSE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), 0);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 0);
- // The variant id is the same
- idSmall.m_mask = RPI::ShaderVariantKey(0);
- idLarge.m_mask = RPI::ShaderVariantKey(0);
- EXPECT_FALSE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), 0);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 0);
- // If the mask is 0, the key has insignificant bits, the variant id is the same
- idSmall.m_mask = RPI::ShaderVariantKey(0);
- idLarge.m_mask = RPI::ShaderVariantKey(0);
- idSmall.m_key = RPI::ShaderVariantKey(31);
- idLarge.m_key = RPI::ShaderVariantKey(15);
- EXPECT_FALSE(idComparator(idSmall, idLarge));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idSmall, idLarge), 0);
- EXPECT_FALSE(idComparator(idLarge, idSmall));
- EXPECT_EQ(RPI::ShaderVariantIdComparator::Compare(idLarge, idSmall), 0);
- }
- TEST_F(ShaderTests, ValidateShaderVariantKeyFallbackPacking)
- {
- AZStd::vector<RPI::ShaderOptionValuePair> idList0;
- idList0.push_back({ Name("Black"), RPI::ShaderOptionValue(0) }); // 1+ bit
- idList0.push_back({ Name("Maroon"), RPI::ShaderOptionValue(1) }); // ...
- idList0.push_back({ Name("Green"), RPI::ShaderOptionValue(2) }); // 2+ bits
- idList0.push_back({ Name("Olive"), RPI::ShaderOptionValue(3) }); // ...
- idList0.push_back({ Name("Navy"), RPI::ShaderOptionValue(4) }); // 3+ bits
- idList0.push_back({ Name("Purple"), RPI::ShaderOptionValue(5) }); // ...
- idList0.push_back({ Name("Teal"), RPI::ShaderOptionValue(6) }); // ...
- idList0.push_back({ Name("Silver"), RPI::ShaderOptionValue(7) }); // ...
- idList0.push_back({ Name("Gray"), RPI::ShaderOptionValue(8) }); // 4+ bits
- idList0.push_back({ Name("Red"), RPI::ShaderOptionValue(9) }); // ...
- idList0.push_back({ Name("Lime"), RPI::ShaderOptionValue(10) }); // ...
- idList0.push_back({ Name("Yellow"), RPI::ShaderOptionValue(11) }); // ...
- idList0.push_back({ Name("Blue"), RPI::ShaderOptionValue(12) }); // ...
- idList0.push_back({ Name("Fuchsia"), RPI::ShaderOptionValue(13) }); // ...
- idList0.push_back({ Name("Cyan"), RPI::ShaderOptionValue(14) }); // ...
- idList0.push_back({ Name("White"), RPI::ShaderOptionValue(15) }); // ...
- idList0.push_back({ Name("Beige"), RPI::ShaderOptionValue(16) }); // 5 bits!!
- // Six descriptors with 5 bits each are 30 bits, but AZSLc will pack them within 32-bit boundaries, so
- // every six descriptors will end up wasting 2 bits of register space.
- // This test checks for values up to 256 bits
- uint32_t bitOffset = 0;
- uint32_t order = 0;
- constexpr uint32_t descriptorsPerElement = 6;
- constexpr uint32_t numberOfElements = RPI::ShaderVariantKeyBitCount / RPI::ShaderElementBitSize;
- RPI::ShaderOptionDescriptor descriptor[numberOfElements * descriptorsPerElement];
- RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
- for (int i = 0; i < numberOfElements * descriptorsPerElement; i++)
- {
- std::stringstream ss;
- ss << "Color" << i;
- descriptor[i] = RPI::ShaderOptionDescriptor{ Name(ss.str().c_str()),
- RPI::ShaderOptionType::Enumeration,
- bitOffset,
- order++,
- idList0,
- Name("Fuchsia") };
- shaderOptionGroupLayout->AddShaderOption(descriptor[i]);
- EXPECT_EQ(descriptor[i].GetBitCount(), 5);
- bitOffset = descriptor[i].GetBitOffset() + descriptor[i].GetBitCount();
- // This hack up-aligns the bit offset to match the AZSLc behavior
- // (AZSLc respects a 32-bit boundary for any options used)
- // It doesn't matter for the test itself since we read raw data
- if (i % descriptorsPerElement == (descriptorsPerElement - 1))
- {
- bitOffset += 2;
- }
- }
- shaderOptionGroupLayout->Finalize();
- // Create and test a few ShaderOptionGroup-s
- // This simple test matches the expected padding for AZSLc and should only be updated
- // if AZSLc.exe changes the shader variant key fallback mask.
- auto shaderOptionGroup = RPI::ShaderOptionGroup(shaderOptionGroupLayout);
- // ShaderVariantKey is 32 or more bits
- if constexpr (numberOfElements >= 1)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color0"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color1"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color2"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color3"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color4"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color5"), AZ::Name("Fuchsia")); // 13
- }
- // ShaderVariantKey is 64 or more bits
- if constexpr (numberOfElements >= 2)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color6"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color7"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color8"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color9"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color10"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color11"), AZ::Name("Fuchsia")); // 13
- }
- // ShaderVariantKey is 96 or more bits
- if constexpr (numberOfElements >= 3)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color12"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color13"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color14"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color15"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color16"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color17"), AZ::Name("Fuchsia")); // 13
- }
- // ShaderVariantKey is 128 or more bits
- if constexpr (numberOfElements >= 4)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color18"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color19"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color20"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color21"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color22"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color23"), AZ::Name("Fuchsia")); // 13
- }
- // ShaderVariantKey is 160 or more bits
- if constexpr (numberOfElements >= 5)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color24"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color25"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color26"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color27"), AZ::Name("Fuchsia")); // 13
- shaderOptionGroup.SetValue(AZ::Name("Color28"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color29"), AZ::Name("Olive")); // 3
- }
- // ShaderVariantKey is 192 or more bits
- if constexpr (numberOfElements >= 6)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color30"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color31"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color32"), AZ::Name("Fuchsia")); // 13
- shaderOptionGroup.SetValue(AZ::Name("Color33"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color34"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color35"), AZ::Name("Navy")); // 4
- }
- // ShaderVariantKey is 224 or more bits
- if constexpr (numberOfElements >= 7)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color36"), AZ::Name("Lime")); // 10
- shaderOptionGroup.SetValue(AZ::Name("Color37"), AZ::Name("Fuchsia")); // 13
- shaderOptionGroup.SetValue(AZ::Name("Color38"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color39"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color40"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color41"), AZ::Name("Teal")); // 6
- }
- // ShaderVariantKey is 256 or more bits
- if constexpr (numberOfElements >= 8)
- {
- shaderOptionGroup.SetValue(AZ::Name("Color42"), AZ::Name("Fuchsia")); // 13
- shaderOptionGroup.SetValue(AZ::Name("Color43"), AZ::Name("Beige")); // 16
- shaderOptionGroup.SetValue(AZ::Name("Color44"), AZ::Name("Olive")); // 3
- shaderOptionGroup.SetValue(AZ::Name("Color45"), AZ::Name("Navy")); // 4
- shaderOptionGroup.SetValue(AZ::Name("Color46"), AZ::Name("Teal")); // 6
- shaderOptionGroup.SetValue(AZ::Name("Color47"), AZ::Name("Lime")); // 10
- }
- uint32_t fallbackValue[RPI::ShaderVariantKeyAlignedBitCount / RPI::ShaderElementBitSize];
- memcpy(fallbackValue, shaderOptionGroup.GetShaderVariantId().m_key.data(), RPI::ShaderVariantKeyBitCount / 8);
- if constexpr (numberOfElements > 0)
- {
- EXPECT_EQ(fallbackValue[0], 0x1aa31070);
- }
- if constexpr (numberOfElements > 1)
- {
- EXPECT_EQ(fallbackValue[1], 0x1aa31203);
- }
- if constexpr (numberOfElements > 2)
- {
- EXPECT_EQ(fallbackValue[2], 0x1aa30e04);
- }
- if constexpr (numberOfElements > 3)
- {
- EXPECT_EQ(fallbackValue[3], 0x1aa20e06);
- }
- if constexpr (numberOfElements > 4)
- {
- EXPECT_EQ(fallbackValue[4], 0x0706a8c4);
- }
- if constexpr (numberOfElements > 5)
- {
- EXPECT_EQ(fallbackValue[5], 0x08383546);
- }
- if constexpr (numberOfElements > 6)
- {
- EXPECT_EQ(fallbackValue[6], 0x0c41c1aa);
- }
- if constexpr (numberOfElements > 7)
- {
- EXPECT_EQ(fallbackValue[7], 0x14620e0d);
- }
- }
- TEST_F(ShaderTests, ShaderAsset_ValidateSearch)
- {
- using namespace AZ;
- using namespace AZ::RPI;
- auto shaderAsset = CreateShaderAsset();
- auto shaderVariantTreeAsset = CreateShaderVariantTreeAssetForSearch(shaderAsset);
- // We expect the following composition:
- // Index 0 - []
- // Index 1 - [Fuchsia]
- // Index 2 - [Fuchsia, Quality::Auto]
- // Index 3 - [Fuchsia, Quality::Auto, 50]
- // Index 4 - [Fuchsia, Quality::Auto, 50, Off]
- // Index 5 - [Fuchsia, Quality::Auto, 50, On]
- // Index 6 - [Teal]
- // Index 7 - [Teal, Quality::Sublime]
- // Let's search it!
- RPI::ShaderOptionGroup shaderOptionGroup(m_shaderOptionGroupLayoutForVariants);
- const uint32_t stableId0 = 0;
- const uint32_t stableId1 = 1;
- const uint32_t stableId2 = 2;
- const uint32_t stableId3 = 3;
- const uint32_t stableId4 = 4;
- const uint32_t stableId5 = 5;
- const uint32_t stableId6 = 6;
- const uint32_t stableId7 = 7;
- // Index 0 - []
- const auto& result0 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_TRUE(result0.IsRoot());
- EXPECT_FALSE(result0.IsFullyBaked());
- EXPECT_EQ(result0.GetStableId().GetIndex(), stableId0);
- // Index 1 - [Fuchsia]
- shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
- const auto& result1 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result1.IsRoot());
- EXPECT_FALSE(result1.IsFullyBaked());
- EXPECT_EQ(result1.GetStableId().GetIndex(), stableId1);
- // Index 2 - [Fuchsia, Quality::Auto]
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Auto"));
- const auto& result2 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result2.IsRoot());
- EXPECT_FALSE(result2.IsFullyBaked());
- EXPECT_EQ(result2.GetStableId().GetIndex(), stableId2);
- // Index 3 - [Fuchsia, Quality::Auto, 50]
- shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
- const auto& result3 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result3.IsRoot());
- EXPECT_FALSE(result3.IsFullyBaked());
- EXPECT_EQ(result3.GetStableId().GetIndex(), stableId3);
- // Index 4 - [Fuchsia, Quality::Auto, 50, Off]
- shaderOptionGroup.SetValue(Name("Raytracing"), Name("Off"));
- const auto& result4 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result4.IsRoot());
- EXPECT_TRUE(result4.IsFullyBaked());
- EXPECT_EQ(result4.GetStableId().GetIndex(), stableId4);
- // Index 5 - [Fuchsia, Quality::Auto, 50, On]
- shaderOptionGroup.SetValue(Name("Raytracing"), Name("On"));
- const auto& result5 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result5.IsRoot());
- EXPECT_TRUE(result5.IsFullyBaked());
- EXPECT_EQ(result5.GetStableId().GetIndex(), stableId5);
- shaderOptionGroup.Clear();
- // Index 6 - [Teal]
- shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
- const auto& result6 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result6.IsRoot());
- EXPECT_FALSE(result6.IsFullyBaked());
- EXPECT_EQ(result6.GetStableId().GetIndex(), stableId6);
- // Index 7 - [Teal, Quality::Sublime]
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
- const auto& result7 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result7.IsRoot());
- EXPECT_FALSE(result7.IsFullyBaked());
- EXPECT_EQ(result7.GetStableId().GetIndex(), stableId7);
- /*
- All searches so far found exactly the node we were looking for
- The next couple of searches will not find the requested node
- and will instead default to its parent, up the tree to the root
- [] [Root]
- / \
- [Color] [Teal] [Fuchsia]
- / \
- [Quality] [Sublime] [Auto]
- /
- [NumberSamples] [50]
- / \
- [Raytracing] [On] [Off]
- */
- // ----------------------------------------
- // [Quality::Poor]
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Poor"));
- // This node doesn't exist, but setting the quality forced Color to its default value, so we expect to get:
- // Index 1 - [Fuchsia]
- const auto& result8 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result8.IsRoot());
- EXPECT_FALSE(result8.IsFullyBaked());
- EXPECT_EQ(result8.GetStableId().GetIndex(), stableId1);
- // ----------------------------------------
- // [Teal, Quality::Poor]
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Poor"));
- shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
- // This node doesn't exist, but we have set both Color and Quality so we expect to get:
- // Index 6 - [Teal]
- const auto& result9 = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(result9.IsRoot());
- EXPECT_FALSE(result9.IsFullyBaked());
- EXPECT_EQ(result9.GetStableId().GetIndex(), stableId6);
- // ----------------------------------------
- // [Navy, Quality::Good]
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Good"));
- shaderOptionGroup.SetValue(Name("Color"), Name("Navy"));
- // This node doesn't exist (Good Navy), its parent (Navy) doesn't exist either so we expect to get the root:
- // Index 0 - []
- const auto& resultA = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_TRUE(resultA.IsRoot());
- EXPECT_FALSE(resultA.IsFullyBaked());
- EXPECT_EQ(resultA.GetStableId().GetIndex(), stableId0);
- // ----------------------------------------
- // [Teal, Quality::Sublime, 50, Off] - Test 1/3
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
- shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
- shaderOptionGroup.SetValue(Name("Raytracing"), Name("Off"));
- // No specialized nodes exist under (Teal, Sublime) so we expect to get that:
- // Index 7 - [Teal, Quality::Sublime]
- const auto& resultB = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(resultB.IsRoot());
- EXPECT_FALSE(resultB.IsFullyBaked());
- EXPECT_EQ(resultB.GetStableId().GetIndex(), stableId7);
- // ----------------------------------------
- // [Teal, Quality::Sublime, 50, On] - Test 2/3
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
- shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
- shaderOptionGroup.SetValue(Name("Raytracing"), Name("On"));
- // No specialized nodes exist under (Teal, Sublime) so we expect to get that:
- // Index 7 - [Teal, Quality::Sublime]
- const auto& resultC = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(resultC.IsRoot());
- EXPECT_FALSE(resultC.IsFullyBaked());
- EXPECT_EQ(resultC.GetStableId().GetIndex(), stableId7);
- // ----------------------------------------
- // [Teal, Quality::Sublime, 150] - Test 3/3
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Color"), Name("Teal"));
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Sublime"));
- shaderOptionGroup.SetValue(Name("NumberSamples"), Name("150"));
- // No specialized nodes exist under (Teal, Sublime) so we expect to get that:
- // Index 7 - [Teal, Quality::Sublime]
- const auto& resultD = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(resultD.IsRoot());
- EXPECT_FALSE(resultD.IsFullyBaked());
- EXPECT_EQ(resultD.GetStableId().GetIndex(), stableId7);
- // ----------------------------------------
- // [120]
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Auto"));
- shaderOptionGroup.SetValue(Name("NumberSamples"), Name("120"));
- // The node (Fuchsia, Auto, 120) doesn't exist - note that the higher order options assume their default values. We get:
- // Index 2 - [Fuchsia, Quality::Auto]
- const auto& resultE = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(resultE.IsRoot());
- EXPECT_FALSE(resultE.IsFullyBaked());
- EXPECT_EQ(resultE.GetStableId().GetIndex(), stableId2);
- // ----------------------------------------
- // [50]
- shaderOptionGroup.Clear();
- shaderOptionGroup.SetValue(Name("Color"), Name("Fuchsia"));
- shaderOptionGroup.SetValue(Name("Quality"), Name("Quality::Auto"));
- shaderOptionGroup.SetValue(Name("NumberSamples"), Name("50"));
- // ----------------------------------------
- shaderOptionGroup.SetValue(Name("Raytracing"), Name("Off"));
- // Index 4 - [Fuchsia, Quality::Auto, 50, Off]
- const auto& resultF = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(resultF.IsRoot());
- EXPECT_TRUE(resultF.IsFullyBaked());
- EXPECT_EQ(resultF.GetStableId().GetIndex(), stableId4);
- shaderOptionGroup.SetValue(Name("Raytracing"), Name("On"));
- // Index 5 - [Fuchsia, Quality::Auto, 50, On]
- const auto& resultG = shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderOptionGroup.GetShaderVariantId());
- EXPECT_FALSE(resultG.IsRoot());
- EXPECT_TRUE(resultG.IsFullyBaked());
- EXPECT_EQ(resultG.GetStableId().GetIndex(), stableId5);
- }
- TEST_F(ShaderTests, ShaderVariantAsset_IsFullyBaked)
- {
- using namespace AZ;
- using namespace AZ::RPI;
- ShaderOptionGroup shaderOptions{m_shaderOptionGroupLayoutForAsset};
- Data::Asset<ShaderVariantAsset> shaderVariantAsset;
- shaderVariantAsset = CreateTestShaderVariantAsset(shaderOptions.GetShaderVariantId(), RPI::ShaderVariantStableId{0}, false);
- EXPECT_FALSE(shaderVariantAsset->IsFullyBaked());
- EXPECT_FALSE(ShaderOptionGroup(m_shaderOptionGroupLayoutForAsset, shaderVariantAsset->GetShaderVariantId()).IsFullySpecified());
- shaderOptions.SetValue(AZ::Name{"Color"}, AZ::Name{"Yellow"});
- shaderOptions.SetValue(AZ::Name{"Quality"}, AZ::Name{"Quality::Average"});
- shaderOptions.SetValue(AZ::Name{"NumberSamples"}, AZ::Name{"100"});
- shaderOptions.SetValue(AZ::Name{"Raytracing"}, AZ::Name{"On"});
- shaderVariantAsset = CreateTestShaderVariantAsset(shaderOptions.GetShaderVariantId(), RPI::ShaderVariantStableId{0}, true);
- EXPECT_TRUE(shaderVariantAsset->IsFullyBaked());
- EXPECT_TRUE(ShaderOptionGroup(m_shaderOptionGroupLayoutForAsset, shaderVariantAsset->GetShaderVariantId()).IsFullySpecified());
- shaderOptions.ClearValue(AZ::Name{"NumberSamples"});
- shaderVariantAsset = CreateTestShaderVariantAsset(shaderOptions.GetShaderVariantId(), RPI::ShaderVariantStableId{0}, false);
- EXPECT_FALSE(shaderVariantAsset->IsFullyBaked());
- EXPECT_FALSE(ShaderOptionGroup(m_shaderOptionGroupLayoutForAsset, shaderVariantAsset->GetShaderVariantId()).IsFullySpecified());
- }
- }
|