/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include namespace UnitTest { using namespace AZ; using namespace RPI; class LuaMaterialFunctorTests : public RPITestFixture { public: static void AddLuaFunctor(MaterialTypeAssetCreator& materialTypeCreator, const AZStd::string& script) { LuaMaterialFunctorSourceData functorSourceData; functorSourceData.m_luaScript = script; MaterialNameContext nameContext; MaterialFunctorSourceData::RuntimeContext createFunctorContext{ "Dummy.materialtype", materialTypeCreator.GetMaterialPropertiesLayout(), materialTypeCreator.GetMaterialShaderResourceGroupLayout(), materialTypeCreator.GetShaderCollection(), &nameContext }; MaterialFunctorSourceData::FunctorResult result = functorSourceData.CreateFunctor(createFunctorContext); EXPECT_TRUE(result.IsSuccess()); if (result.IsSuccess()) { materialTypeCreator.AddMaterialFunctor(result.GetValue()); for (auto& shaderOption : functorSourceData.GetShaderOptionDependencies()) { materialTypeCreator.ClaimShaderOptionOwnership(shaderOption); } } } protected: void SetUp() override { RPITestFixture::SetUp(); } void TearDown() override { RPITestFixture::TearDown(); } AZ::RPI::Ptr CreateCommonTestShaderOptionsLayout() { AZStd::vector boolOptionValues = CreateBoolShaderOptionValues(); AZStd::vector intRangeOptionValues = CreateIntRangeShaderOptionValues(0, 15); AZStd::vector qualityOptionValues = CreateEnumShaderOptionValues({"Quality::Low", "Quality::Medium", "Quality::High"}); AZ::RPI::Ptr shaderOptions = RPI::ShaderOptionGroupLayout::Create(); shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_bool"}, ShaderOptionType::Boolean, 0, 0, boolOptionValues, Name{"False"}}); shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_uint"}, ShaderOptionType::IntegerRange, 1, 1, intRangeOptionValues, Name{"0"}}); shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enum"}, ShaderOptionType::Enumeration, 5, 2, qualityOptionValues, Name{"Quality::Low"}}); shaderOptions->Finalize(); return shaderOptions; } }; class TestMaterialData { public: // Setup for a single material property and nothing else, used in particular to test setting render states void Setup( MaterialPropertyDataType dataType, const char* materialPropertyName, const char* luaFunctorScript) { MaterialTypeAssetCreator materialTypeCreator; materialTypeCreator.Begin(Uuid::CreateRandom()); materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom()), Name{"TestShader"}); materialTypeCreator.BeginMaterialProperty(Name{materialPropertyName}, dataType); materialTypeCreator.EndMaterialProperty(); LuaMaterialFunctorTests::AddLuaFunctor(materialTypeCreator, luaFunctorScript); EXPECT_TRUE(materialTypeCreator.End(m_materialTypeAsset)); Data::Asset materialAsset; MaterialAssetCreator materialCreator; materialCreator.Begin(Uuid::CreateRandom(), m_materialTypeAsset, true); EXPECT_TRUE(materialCreator.End(materialAsset)); m_material = Material::Create(materialAsset); m_materialPropertyIndex = m_material->FindPropertyIndex(Name{materialPropertyName}); } // Setup for a single material property and a specific shader constant input void Setup( RHI::Ptr materialSrgLayout, MaterialPropertyDataType dataType, const char* materialPropertyName, const char* shaderInputName, const char* luaFunctorScript) { MaterialTypeAssetCreator materialTypeCreator; materialTypeCreator.Begin(Uuid::CreateRandom()); materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout)); materialTypeCreator.BeginMaterialProperty(Name{materialPropertyName}, dataType); materialTypeCreator.EndMaterialProperty(); LuaMaterialFunctorTests::AddLuaFunctor(materialTypeCreator, luaFunctorScript); EXPECT_TRUE(materialTypeCreator.End(m_materialTypeAsset)); Data::Asset materialAsset; MaterialAssetCreator materialCreator; materialCreator.Begin(Uuid::CreateRandom(),m_materialTypeAsset, true); EXPECT_TRUE(materialCreator.End(materialAsset)); m_material = Material::Create(materialAsset); m_materialPropertyIndex = m_material->FindPropertyIndex(Name{materialPropertyName}); m_srgConstantIndex = m_material->GetRHIShaderResourceGroup()->GetData().FindShaderInputConstantIndex(Name{shaderInputName}); } // Setup for a single material property and a specific shader option void Setup( AZ::RPI::Ptr shaderOptionsLayout, MaterialPropertyDataType dataType, const char* materialPropertyName, const char* shaderOptionName, const char* luaFunctorScript) { MaterialTypeAssetCreator materialTypeCreator; materialTypeCreator.Begin(Uuid::CreateRandom()); materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom(), {}, shaderOptionsLayout), Name{"TestShader"}); materialTypeCreator.BeginMaterialProperty(Name{materialPropertyName}, dataType); materialTypeCreator.EndMaterialProperty(); LuaMaterialFunctorTests::AddLuaFunctor(materialTypeCreator, luaFunctorScript); EXPECT_TRUE(materialTypeCreator.End(m_materialTypeAsset)); Data::Asset materialAsset; MaterialAssetCreator materialCreator; materialCreator.Begin(Uuid::CreateRandom(), m_materialTypeAsset, true); EXPECT_TRUE(materialCreator.End(materialAsset)); m_material = Material::Create(materialAsset); m_materialPropertyIndex = m_material->FindPropertyIndex(Name{materialPropertyName}); m_shaderOptionIndex = shaderOptionsLayout->FindShaderOptionIndex(Name{shaderOptionName}); } // Setup for two material properties for testing one property affecting another property's metadata void Setup( MaterialPropertyDataType primaryPropertyDataType, const char* primaryPropertyName, MaterialPropertyDataType secondaryPropertyDataType, const char* secondaryPropertyName, const char* luaFunctorScript) { MaterialTypeAssetCreator materialTypeCreator; materialTypeCreator.Begin(Uuid::CreateRandom()); materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom())); materialTypeCreator.BeginMaterialProperty(Name{primaryPropertyName}, primaryPropertyDataType); materialTypeCreator.EndMaterialProperty(); materialTypeCreator.BeginMaterialProperty(Name{secondaryPropertyName}, secondaryPropertyDataType); materialTypeCreator.EndMaterialProperty(); LuaMaterialFunctorTests::AddLuaFunctor(materialTypeCreator, luaFunctorScript); EXPECT_TRUE(materialTypeCreator.End(m_materialTypeAsset)); Data::Asset materialAsset; MaterialAssetCreator materialCreator; materialCreator.Begin(Uuid::CreateRandom(), m_materialTypeAsset, true); EXPECT_TRUE(materialCreator.End(materialAsset)); m_material = Material::Create(materialAsset); m_materialPropertyIndex = m_material->FindPropertyIndex(Name{primaryPropertyName}); m_otherMaterialPropertyIndex = m_material->FindPropertyIndex(Name{secondaryPropertyName}); } Data::Asset GetMaterialTypeAsset() { return m_materialTypeAsset; } Data::Instance GetMaterial() { return m_material; } MaterialPropertyIndex GetMaterialPropertyIndex() { return m_materialPropertyIndex; } MaterialPropertyIndex GetOtherMaterialPropertyIndex() { return m_otherMaterialPropertyIndex; } RHI::ShaderInputConstantIndex GetSrgConstantIndex() { return m_srgConstantIndex; } ShaderOptionIndex GetShaderOptionIndex() { return m_shaderOptionIndex; } private: Data::Asset m_materialTypeAsset; Data::Instance m_material; MaterialPropertyIndex m_materialPropertyIndex; MaterialPropertyIndex m_otherMaterialPropertyIndex; RHI::ShaderInputConstantIndex m_srgConstantIndex; ShaderOptionIndex m_shaderOptionIndex; }; TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Bool) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestBool"} end function Process(context) local value = context:GetMaterialPropertyValue_bool("general.TestBool") context:SetShaderConstant_bool("m_bool", value) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Bool, "general.TestBool", "m_bool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(true, testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(false, testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Float) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestFloat"} end function Process(context) local value = context:GetMaterialPropertyValue_float("general.TestFloat") context:SetShaderConstant_float("m_float", value * 2.0) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Float, "general.TestFloat", "m_float", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{1.25f}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_FLOAT_EQ(2.5f, testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Int) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestInt"} end function Process(context) local value = context:GetMaterialPropertyValue_int("general.TestInt") context:SetShaderConstant_int("m_int", value * -1) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Int, "general.TestInt", "m_int", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{2}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(-2, testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_UInt) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestUInt"} end function Process(context) local value = context:GetMaterialPropertyValue_uint("general.TestUInt") context:SetShaderConstant_uint("m_uint", value + 5) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::UInt, "general.TestUInt", "m_uint", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{2u}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(7, testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Float2) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestVector2"} end function Process(context) local value = context:GetMaterialPropertyValue_Vector2("general.TestVector2") local swap = value.y value.y = value.x value.x = swap context:SetShaderConstant_Vector2("m_float2", value) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Vector2, "general.TestVector2", "m_float2", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{Vector2(1.0f, 2.0f)}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(Vector2(2.0f, 1.0f), testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Vector3) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestVector3"} end function Process(context) local value = context:GetMaterialPropertyValue_Vector3("general.TestVector3") value:Normalize() context:SetShaderConstant_Vector3("m_float3", value) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Vector3, "general.TestVector3", "m_float3", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{Vector3(5.0f, 4.0f, 3.0f)}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(Vector3(5.0f, 4.0f, 3.0f).GetNormalized(), testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Vector4) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestVector4"} end function Process(context) local value = context:GetMaterialPropertyValue_Vector4("general.TestVector4") value:Homogenize() context:SetShaderConstant_Vector4("m_float4", value) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Vector4, "general.TestVector4", "m_float4", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{Vector4(1.0f, 2.0f, 3.0f, 4.0f)}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(Vector4(1.0f, 2.0f, 3.0f, 4.0f) / 4.0f, testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_GetMaterialProperty_SetShaderConstant_Color) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestColor"} end function Process(context) local value = context:GetMaterialPropertyValue_Color("general.TestColor") value.r = value.r * value.a value.g = value.g * value.a value.b = value.b * value.a context:SetShaderConstant_Color("m_color", value) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Color, "general.TestColor", "m_color", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{Color(1.0f, 0.5f, 0.4f, 0.5f)}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(Color(0.5f, 0.25f, 0.2f, 0.5f), testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderConstant_Matrix3x3) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.Scale"} end function Process(context) local scale = context:GetMaterialPropertyValue_float("general.Scale") local tansform = Matrix3x3.CreateScale(Vector3(scale, scale, 1.0)) context:SetShaderConstant_Matrix3x3("m_float3x3", tansform) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Float, "general.Scale", "m_float3x3", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{0.5f}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(Matrix3x3::CreateScale(Vector3(0.5f, 0.5f, 1.0f)), testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderConstant_Matrix4x4) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.Offset"} end function Process(context) local offset = context:GetMaterialPropertyValue_Vector3("general.Offset") local tansform = Matrix4x4.CreateTranslation(offset) context:SetShaderConstant_Matrix4x4("m_float4x4", tansform) end )"; auto materialSrgLayout = CreateCommonTestMaterialSrgLayout(); auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout); TestMaterialData testData; testData.Setup(materialSrgLayout, MaterialPropertyDataType::Vector3, "general.Offset", "m_float4x4", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{Vector3(1.0f, 2.0f, 3.0f)}); ProcessQueuedSrgCompilations(shaderAsset, materialSrgLayout->GetName()); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(Matrix4x4::CreateTranslation(Vector3(1.0f, 2.0f, 3.0f)), testData.GetMaterial()->GetRHIShaderResourceGroup()->GetData().GetConstant(testData.GetSrgConstantIndex())); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderOption_Bool) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestBool"} end function GetShaderOptionDependencies() return {"o_bool"} end function Process(context) local value = context:GetMaterialPropertyValue_bool("general.TestBool") context:SetShaderOptionValue_bool("o_bool", value) end )"; AZ::RPI::Ptr options = CreateCommonTestShaderOptionsLayout(); TestMaterialData testData; testData.Setup(options, MaterialPropertyDataType::Bool, "general.TestBool", "o_bool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex()); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(0, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderOption_UInt) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestInt"} end function GetShaderOptionDependencies() return {"o_uint"} end function Process(context) local value = context:GetMaterialPropertyValue_int("general.TestInt") context:SetShaderOptionValue_uint("o_uint", value * 2) end )"; AZ::RPI::Ptr options = CreateCommonTestShaderOptionsLayout(); TestMaterialData testData; testData.Setup(options, MaterialPropertyDataType::Int, "general.TestInt", "o_uint", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{6}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(12, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_uint"}).GetIndex()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderOption_Enum) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.UseHighQuality"} end function GetShaderOptionDependencies() return {"o_enum"} end function Process(context) local useHighQuality = context:GetMaterialPropertyValue_bool("general.UseHighQuality") if (useHighQuality) then context:SetShaderOptionValue_enum("o_enum", "Quality::High") else context:SetShaderOptionValue_enum("o_enum", "Quality::Medium") end end )"; AZ::RPI::Ptr options = CreateCommonTestShaderOptionsLayout(); TestMaterialData testData; testData.Setup(options, MaterialPropertyDataType::Bool, "general.UseHighQuality", "o_enum", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(2, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex()); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_ShaderItem_SetShaderOption_Bool) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestBool"} end function GetShaderOptionDependencies() return {"o_bool"} end function Process(context) local value = context:GetMaterialPropertyValue_bool("general.TestBool") context:GetShaderByTag("TestShader"):SetShaderOptionValue_bool("o_bool", value) end )"; AZ::RPI::Ptr options = CreateCommonTestShaderOptionsLayout(); TestMaterialData testData; testData.Setup(options, MaterialPropertyDataType::Bool, "general.TestBool", "o_bool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ( 1, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex()); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ( 0, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_ShaderItem_SetShaderOption_UInt) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.TestInt"} end function GetShaderOptionDependencies() return {"o_uint"} end function Process(context) local value = context:GetMaterialPropertyValue_int("general.TestInt") context:GetShaderByTag("TestShader"):SetShaderOptionValue_uint("o_uint", value * 2) end )"; AZ::RPI::Ptr options = CreateCommonTestShaderOptionsLayout(); TestMaterialData testData; testData.Setup(options, MaterialPropertyDataType::Int, "general.TestInt", "o_uint", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{6}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ( 12, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_uint"}).GetIndex()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_ShaderItem_SetShaderOption_Enum) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.UseHighQuality"} end function GetShaderOptionDependencies() return {"o_enum"} end function Process(context) local useHighQuality = context:GetMaterialPropertyValue_bool("general.UseHighQuality") if (useHighQuality) then context:GetShaderByTag("TestShader"):SetShaderOptionValue_enum("o_enum", "Quality::High") else context:GetShaderByTag("TestShader"):SetShaderOptionValue_enum("o_enum", "Quality::Medium") end end )"; AZ::RPI::Ptr options = CreateCommonTestShaderOptionsLayout(); TestMaterialData testData; testData.Setup(options, MaterialPropertyDataType::Bool, "general.UseHighQuality", "o_enum", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ( 2, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex()); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ( 1, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_EditorContext_SetMaterialPropertyVisibility) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return { "general.mode" } end function ProcessEditor(context) local mode = context:GetMaterialPropertyValue_uint("general.mode") if (mode == 1) then context:SetMaterialPropertyVisibility("general.value", MaterialPropertyVisibility_Enabled) elseif (mode == 2) then context:SetMaterialPropertyVisibility("general.value", MaterialPropertyVisibility_Disabled) else context:SetMaterialPropertyVisibility("general.value", MaterialPropertyVisibility_Hidden) end end )"; TestMaterialData testData; testData.Setup( MaterialPropertyDataType::UInt, "general.mode", MaterialPropertyDataType::Float, "general.value", functorScript); AZStd::unordered_set changedPropertyNames; AZStd::unordered_map propertyDynamicMetadata; propertyDynamicMetadata[Name{"general.mode"}] = {}; propertyDynamicMetadata[Name{"general.value"}] = {}; AZStd::unordered_set changedPropertyGroupNames; AZStd::unordered_map propertyGroupDynamicMetadata; propertyGroupDynamicMetadata[Name{"general"}] = {}; Ptr functor = testData.GetMaterialTypeAsset()->GetMaterialFunctors()[0]; AZ::RPI::MaterialFunctor::EditorContext context = AZ::RPI::MaterialFunctor::EditorContext( testData.GetMaterial()->GetPropertyValues(), testData.GetMaterial()->GetMaterialPropertiesLayout(), propertyDynamicMetadata, propertyGroupDynamicMetadata, changedPropertyNames, changedPropertyGroupNames, &functor->GetMaterialPropertyDependencies() ); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{0u}); functor->Process(context); EXPECT_EQ(MaterialPropertyVisibility::Hidden, propertyDynamicMetadata[Name{"general.value"}].m_visibility); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{1u}); functor->Process(context); EXPECT_EQ(MaterialPropertyVisibility::Enabled, propertyDynamicMetadata[Name{"general.value"}].m_visibility); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{2u}); functor->Process(context); EXPECT_EQ(MaterialPropertyVisibility::Disabled, propertyDynamicMetadata[Name{"general.value"}].m_visibility); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_EditorContext_SetMaterialPropertyDescriptionAndRanges) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return { "general.units" } end function ProcessEditor(context) local units = context:GetMaterialPropertyValue_uint("general.units") if (units == 0) then context:SetMaterialPropertyDescription("general.distance", "Distance in meters") context:SetMaterialPropertyMinValue_float("general.distance", -10) context:SetMaterialPropertyMaxValue_float("general.distance", 10) context:SetMaterialPropertySoftMinValue_float("general.distance", -1) context:SetMaterialPropertySoftMaxValue_float("general.distance", 1) else context:SetMaterialPropertyDescription("general.distance", "Distance in centimeters") context:SetMaterialPropertyMinValue_float("general.distance", -1000) context:SetMaterialPropertyMaxValue_float("general.distance", 1000) context:SetMaterialPropertySoftMinValue_float("general.distance", -100) context:SetMaterialPropertySoftMaxValue_float("general.distance", 100) end end )"; TestMaterialData testData; testData.Setup( MaterialPropertyDataType::UInt, "general.units", MaterialPropertyDataType::Float, "general.distance", functorScript); AZStd::unordered_set changedPropertyNames; AZStd::unordered_map propertyDynamicMetadata; propertyDynamicMetadata[Name{"general.units"}] = {}; propertyDynamicMetadata[Name{"general.distance"}] = {}; AZStd::unordered_set changedPropertyGroupNames; AZStd::unordered_map propertyGroupDynamicMetadata; propertyGroupDynamicMetadata[Name{"general"}] = {}; Ptr functor = testData.GetMaterialTypeAsset()->GetMaterialFunctors()[0]; AZ::RPI::MaterialFunctor::EditorContext context = AZ::RPI::MaterialFunctor::EditorContext( testData.GetMaterial()->GetPropertyValues(), testData.GetMaterial()->GetMaterialPropertiesLayout(), propertyDynamicMetadata, propertyGroupDynamicMetadata, changedPropertyNames, changedPropertyGroupNames, &functor->GetMaterialPropertyDependencies() ); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{0u}); functor->Process(context); EXPECT_STREQ("Distance in meters", propertyDynamicMetadata[Name{"general.distance"}].m_description.c_str()); EXPECT_EQ(-10.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_min.GetValue()); EXPECT_EQ(10.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_max.GetValue()); EXPECT_EQ(-1.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_softMin.GetValue()); EXPECT_EQ(1.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_softMax.GetValue()); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{1u}); functor->Process(context); EXPECT_STREQ("Distance in centimeters", propertyDynamicMetadata[Name{"general.distance"}].m_description.c_str()); EXPECT_EQ(-1000.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_min.GetValue()); EXPECT_EQ(1000.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_max.GetValue()); EXPECT_EQ(-100.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_softMin.GetValue()); EXPECT_EQ(100.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_softMax.GetValue()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_EditorContext_SetMaterialPropertyGroupVisibility) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return { "general.mode" } end function ProcessEditor(context) local mode = context:GetMaterialPropertyValue_uint("general.mode") if (mode == 1) then context:SetMaterialPropertyGroupVisibility("otherGroup", MaterialPropertyGroupVisibility_Enabled) else context:SetMaterialPropertyGroupVisibility("otherGroup", MaterialPropertyGroupVisibility_Hidden) end end )"; TestMaterialData testData; testData.Setup( MaterialPropertyDataType::UInt, "general.mode", MaterialPropertyDataType::Float, "otherGroup.value", functorScript); AZStd::unordered_set changedPropertyNames; AZStd::unordered_map propertyDynamicMetadata; propertyDynamicMetadata[Name{"general.mode"}] = {}; propertyDynamicMetadata[Name{"otherGroup.value"}] = {}; AZStd::unordered_set changedPropertyGroupNames; AZStd::unordered_map propertyGroupDynamicMetadata; propertyGroupDynamicMetadata[Name{"general"}] = {}; propertyGroupDynamicMetadata[Name{"otherGroup"}] = {}; Ptr functor = testData.GetMaterialTypeAsset()->GetMaterialFunctors()[0]; AZ::RPI::MaterialFunctor::EditorContext context = AZ::RPI::MaterialFunctor::EditorContext( testData.GetMaterial()->GetPropertyValues(), testData.GetMaterial()->GetMaterialPropertiesLayout(), propertyDynamicMetadata, propertyGroupDynamicMetadata, changedPropertyNames, changedPropertyGroupNames, &functor->GetMaterialPropertyDependencies() ); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{0u}); functor->Process(context); EXPECT_EQ(MaterialPropertyGroupVisibility::Enabled, propertyGroupDynamicMetadata[Name{"general"}].m_visibility); EXPECT_EQ(MaterialPropertyGroupVisibility::Hidden, propertyGroupDynamicMetadata[Name{"otherGroup"}].m_visibility); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{1u}); functor->Process(context); EXPECT_EQ(MaterialPropertyGroupVisibility::Enabled, propertyGroupDynamicMetadata[Name{"general"}].m_visibility); EXPECT_EQ(MaterialPropertyGroupVisibility::Enabled, propertyGroupDynamicMetadata[Name{"otherGroup"}].m_visibility); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetRenderStates) { using namespace AZ::RPI; // We aren't testing every single render state here, but just getting a representative set... const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function Process(context) local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") if(boolValue) then context:GetShader(0):GetRenderStatesOverride():SetMultisampleCustomPositionCount(1) context:GetShader(0):GetRenderStatesOverride():SetMultisampleCustomPosition(0, 2, 4) context:GetShader(0):GetRenderStatesOverride():SetCullMode(CullMode_None) context:GetShader(0):GetRenderStatesOverride():SetBlendEnabled(1, true) context:GetShader(0):GetRenderStatesOverride():SetDepthBias(-1) context:GetShader(0):GetRenderStatesOverride():SetDepthBiasClamp(0.2) context:GetShader(0):GetRenderStatesOverride():SetStencilWriteMask(0xF0) else context:GetShader(0):GetRenderStatesOverride():ClearMultisampleCustomPositionCount() context:GetShader(0):GetRenderStatesOverride():ClearMultisampleCustomPosition(0) context:GetShader(0):GetRenderStatesOverride():ClearCullMode() context:GetShader(0):GetRenderStatesOverride():ClearBlendEnabled(1) context:GetShader(0):GetRenderStatesOverride():ClearDepthBias() context:GetShader(0):GetRenderStatesOverride():ClearDepthBiasClamp() context:GetShader(0):GetRenderStatesOverride():ClearStencilWriteMask() end end )"; TestMaterialData testData; testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositionsCount); EXPECT_EQ(2, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_x); EXPECT_EQ(4, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_y); EXPECT_EQ(RHI::CullMode::None, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_cullMode); EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_blendState.m_targets[1].m_enable); EXPECT_EQ(-1, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBias); EXPECT_FLOAT_EQ(0.2, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBiasClamp); EXPECT_EQ(0xF0, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_depthStencilState.m_stencil.m_writeMask); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(RHI::RenderStates_InvalidUInt, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositionsCount); EXPECT_EQ(RHI::Limits::Pipeline::MultiSampleCustomLocationGridSize, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_x); EXPECT_EQ(RHI::Limits::Pipeline::MultiSampleCustomLocationGridSize, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_y); EXPECT_EQ(RHI::CullMode::Invalid, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_cullMode); EXPECT_EQ(RHI::RenderStates_InvalidBool, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_blendState.m_targets[1].m_enable); EXPECT_EQ(RHI::RenderStates_InvalidInt, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBias); EXPECT_FLOAT_EQ(RHI::RenderStates_InvalidFloat, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBiasClamp); EXPECT_EQ(RHI::RenderStates_InvalidUInt, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_depthStencilState.m_stencil.m_writeMask); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderEnabledByTag) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function Process(context) local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") context:GetShaderByTag("TestShader"):SetEnabled(boolValue) end )"; TestMaterialData testData; testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); EXPECT_EQ(true, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].IsEnabled()); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderDrawListTagOverride) { using namespace AZ::RPI; RHI::DrawListTagRegistry* drawListTagRegistry = RHI::RHISystemInterface::Get()->GetDrawListTagRegistry(); drawListTagRegistry->AcquireTag(Name{"TestDrawListTag"}); const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function Process(context) context:GetShaderByTag("TestShader"):SetDrawListTagOverride("TestDrawListTag") end )"; TestMaterialData testData; testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); EXPECT_TRUE(testData.GetMaterial()->Compile()); RHI::DrawListTag tag = drawListTagRegistry->FindTag(Name{"TestDrawListTag"}); EXPECT_EQ(tag, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetDrawListTagOverride()); drawListTagRegistry->ReleaseTag(tag); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_PsoChangesNotAllowed_Error) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function GetShaderOptionDependencies() return {} end function Process(context) local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") if(boolValue) then context:GetShader(0):GetRenderStatesOverride():SetFillMode(FillMode_Wireframe) else context:GetShader(0):GetRenderStatesOverride():ClearFillMode() end end )"; TestMaterialData testData; testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); ErrorMessageFinder errorMessageFinder; errorMessageFinder.AddExpectedErrorMessage("not be changed at runtime because they impact Pipeline State Objects: general.MyBool"); EXPECT_TRUE(testData.GetMaterial()->Compile()); errorMessageFinder.CheckExpectedErrorsFound(); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_MultisampleCustomPositionCountIndex_Error) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function GetShaderOptionDependencies() return {} end function Process(context) local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") if(boolValue) then context:GetShader(0):GetRenderStatesOverride():SetMultisampleCustomPositionCount(20) else context:GetShader(0):GetRenderStatesOverride():ClearMultisampleCustomPositionCount() end end )"; TestMaterialData testData; testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); testData.GetMaterial()->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); ErrorMessageFinder errorMessageFinder; errorMessageFinder.AddExpectedErrorMessage("SetMultisampleCustomPositionCount(20) value is out of range. Must be less than 16."); EXPECT_TRUE(testData.GetMaterial()->Compile()); errorMessageFinder.CheckExpectedErrorsFound(); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_MultisampleCustomPositionIndex_Error) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function GetShaderOptionDependencies() return {} end function Process(context) local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") if(boolValue) then context:GetShader(0):GetRenderStatesOverride():SetMultisampleCustomPosition(17, 0, 0) else context:GetShader(0):GetRenderStatesOverride():ClearMultisampleCustomPosition(18) end end )"; TestMaterialData testData; ErrorMessageFinder errorMessageFinder; errorMessageFinder.AddExpectedErrorMessage("ClearMultisampleCustomPosition(18,...) index is out of range. Must be less than 16."); testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); errorMessageFinder.CheckExpectedErrorsFound(); testData.GetMaterial()->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); errorMessageFinder.AddExpectedErrorMessage("SetMultisampleCustomPosition(17,...) index is out of range. Must be less than 16."); EXPECT_TRUE(testData.GetMaterial()->Compile()); errorMessageFinder.CheckExpectedErrorsFound(); } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_BlendStateIndex_Error) { using namespace AZ::RPI; const char* functorScript = R"( function GetMaterialPropertyDependencies() return {"general.MyBool"} end function GetShaderOptionDependencies() return {} end function Process(context) local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") if(boolValue) then context:GetShader(0):GetRenderStatesOverride():SetBlendEnabled(9, false) else context:GetShader(0):GetRenderStatesOverride():ClearBlendEnabled(10) end end )"; TestMaterialData testData; ErrorMessageFinder errorMessageFinder; errorMessageFinder.AddExpectedErrorMessage("ClearBlendEnabled(10,...) index is out of range. Must be less than 8."); testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); errorMessageFinder.CheckExpectedErrorsFound(); testData.GetMaterial()->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); errorMessageFinder.AddExpectedErrorMessage("SetBlendEnabled(9,...) index is out of range. Must be less than 8."); EXPECT_TRUE(testData.GetMaterial()->Compile()); errorMessageFinder.CheckExpectedErrorsFound(); } }