3c331e00ff
MaterialAssignment::ApplyProperties() still reports warnings but does not update the m_propertyOverrides. MaterialAssignment::ApplyProperties() will now skip the old name'd overrides if overrides are present for the new names. I'm not sure if this will ever happen, but it did happen while I had some intermediate changes, so I imagine it could happen again. I had to update the Material::FindPropertyIndex function to expose information about renames when they occur. This should make it easier for other systems to get (somewhat) automatic benefit from the version update feature. I also found that there was an issue with material inspector where it wouldn't be initialized the the right override values when renames were present. Now it applies the renames to whatever override data it gets from the Material Component. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
892 lines
53 KiB
C++
892 lines
53 KiB
C++
/*
|
|
* 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 <Common/RPITestFixture.h>
|
|
#include <Common/ErrorMessageFinder.h>
|
|
#include <Common/ShaderAssetTestUtils.h>
|
|
#include <Material/MaterialAssetTestUtils.h>
|
|
|
|
#include <Atom/RPI.Public/ColorManagement/TransformColor.h>
|
|
#include <Atom/RPI.Public/Material/Material.h>
|
|
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
|
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
|
#include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
|
|
#include <Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h>
|
|
#include <Atom/RPI.Reflect/Image/ImageMipChainAssetCreator.h>
|
|
#include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
|
|
|
|
namespace UnitTest
|
|
{
|
|
using namespace AZ;
|
|
using namespace RPI;
|
|
|
|
class MaterialTests
|
|
: public RPITestFixture
|
|
{
|
|
protected:
|
|
Data::Asset<ShaderAsset> m_testMaterialShaderAsset;
|
|
AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> m_testMaterialSrgLayout;
|
|
Data::Asset<MaterialTypeAsset> m_testMaterialTypeAsset;
|
|
Data::Asset<MaterialAsset> m_testMaterialAsset;
|
|
Data::Asset<StreamingImageAsset> m_testImageAsset;
|
|
Data::Instance<StreamingImage> m_testImage;
|
|
|
|
Data::Asset<StreamingImageAsset> CreateTestImageAsset() const
|
|
{
|
|
Data::Asset<StreamingImageAsset> testImageAsset;
|
|
|
|
Data::Asset<ImageMipChainAsset> mipChainAsset;
|
|
ImageMipChainAssetCreator mipChainCreator;
|
|
mipChainCreator.Begin(Uuid::CreateRandom(), 1, 1);
|
|
mipChainCreator.BeginMip(RHI::GetImageSubresourceLayout(RHI::Size{ 1,1,1 }, RHI::Format::R8_UNORM));
|
|
uint8_t pixel = 0;
|
|
mipChainCreator.AddSubImage(&pixel, 1);
|
|
mipChainCreator.EndMip();
|
|
mipChainCreator.End(mipChainAsset);
|
|
|
|
StreamingImageAssetCreator imageCreator;
|
|
imageCreator.Begin(Uuid::CreateRandom());
|
|
imageCreator.AddMipChainAsset(*mipChainAsset.Get());
|
|
imageCreator.SetFlags(StreamingImageFlags::NotStreamable);
|
|
imageCreator.SetPoolAssetId(ImageSystemInterface::Get()->GetSystemStreamingPool()->GetAssetId());
|
|
imageCreator.End(testImageAsset);
|
|
|
|
return testImageAsset;
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
RPITestFixture::SetUp();
|
|
|
|
m_testMaterialSrgLayout = CreateCommonTestMaterialSrgLayout();
|
|
m_testMaterialShaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout);
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(m_testMaterialShaderAsset);
|
|
AddCommonTestMaterialProperties(materialTypeCreator);
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyFloat2" }, Vector2{ 10.1f, 10.2f });
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyFloat3" }, Vector3{ 11.1f, 11.2f, 11.3f });
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyFloat4" }, Vector4{ 12.1f, 12.2f, 12.3f, 12.4f });
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyColor" }, Color{ 0.1f, 0.2f, 0.3f, 0.4f });
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyInt" }, -12);
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyUInt" }, 112u);
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyFloat" }, 11.5f);
|
|
materialTypeCreator.SetPropertyValue(Name{ "MyEnum" }, 1u);
|
|
materialTypeCreator.End(m_testMaterialTypeAsset);
|
|
|
|
m_testImageAsset = CreateTestImageAsset();
|
|
m_testImage = StreamingImage::FindOrCreate(m_testImageAsset);
|
|
|
|
MaterialAssetCreator materialCreator;
|
|
materialCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat2" }, Vector2{ 0.1f, 0.2f });
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat3" }, Vector3{ 1.1f, 1.2f, 1.3f });
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat4" }, Vector4{ 2.1f, 2.2f, 2.3f, 2.4f });
|
|
materialCreator.SetPropertyValue(Name{ "MyColor" }, Color{ 1.0f, 1.0f, 1.0f, 1.0f });
|
|
materialCreator.SetPropertyValue(Name{ "MyInt" }, -2);
|
|
materialCreator.SetPropertyValue(Name{ "MyUInt" }, 12u);
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat" }, 1.5f);
|
|
materialCreator.SetPropertyValue(Name{ "MyBool" }, true);
|
|
materialCreator.SetPropertyValue(Name{ "MyImage" }, Data::Asset<ImageAsset>(m_testImageAsset));
|
|
materialCreator.SetPropertyValue(Name{ "MyEnum" }, 2u);
|
|
materialCreator.End(m_testMaterialAsset);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_testMaterialShaderAsset.Reset();
|
|
m_testMaterialSrgLayout = nullptr;
|
|
m_testMaterialTypeAsset.Reset();
|
|
m_testMaterialAsset.Reset();
|
|
m_testImageAsset.Reset();
|
|
m_testImage = nullptr;
|
|
|
|
RPITestFixture::TearDown();
|
|
}
|
|
|
|
void ValidateInitialValuesFromMaterialType(Data::Instance<Material> material)
|
|
{
|
|
// Test reading the values directly...
|
|
|
|
EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" })), false);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -12);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" })), 112u);
|
|
EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 11.5f);
|
|
EXPECT_EQ(material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" })), Vector2(10.1f, 10.2f));
|
|
EXPECT_EQ(material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" })), Vector3(11.1f, 11.2f, 11.3f));
|
|
EXPECT_EQ(material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
|
|
EXPECT_EQ(material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" })), Color(0.1f, 0.2f, 0.3f, 0.4f));
|
|
EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" })), nullptr);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" })), 1u);
|
|
|
|
// Dig in to the SRG to make sure the values were applied there as well...
|
|
|
|
const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
|
|
const RHI::ShaderResourceGroupData& srgData = srg->GetData();
|
|
|
|
EXPECT_EQ(srgData.GetConstant<bool>(srgData.FindShaderInputConstantIndex(Name{ "m_bool" })), false);
|
|
EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), -12);
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 112u);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 11.5f);
|
|
EXPECT_EQ(srgData.GetConstant<Vector2>(srgData.FindShaderInputConstantIndex(Name{ "m_float2" })), Vector2(10.1f, 10.2f));
|
|
// Currently srgData.GetConstant<Vector3> isn't supported so we check the individual floats
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 0), 11.1f);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 1), 11.2f);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 2), 11.3f);
|
|
EXPECT_EQ(srgData.GetConstant<Vector4>(srgData.FindShaderInputConstantIndex(Name{ "m_float4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
|
|
EXPECT_EQ(srgData.GetConstant<Color>(srgData.FindShaderInputConstantIndex(Name{ "m_color" })), TransformColor(Color(0.1f, 0.2f, 0.3f, 0.4f), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg));
|
|
|
|
EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), nullptr);
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 1u);
|
|
}
|
|
|
|
void ValidateInitialValuesFromMaterial(Data::Instance<Material> material)
|
|
{
|
|
// Test reading the values directly...
|
|
|
|
EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" })), true);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -2);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" })), 12u);
|
|
EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 1.5f);
|
|
EXPECT_EQ(material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" })), Vector2(0.1f, 0.2f));
|
|
EXPECT_EQ(material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" })), Vector3(1.1f, 1.2f, 1.3f));
|
|
EXPECT_EQ(material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" })), Vector4(2.1f, 2.2f, 2.3f, 2.4f));
|
|
EXPECT_EQ(material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" })), Color(1.0f, 1.0f, 1.0f, 1.0f));
|
|
EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" })), m_testImage);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" })), 2u);
|
|
|
|
// Dig in to the SRG to make sure the values were applied there as well...
|
|
|
|
const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
|
|
const RHI::ShaderResourceGroupData& srgData = srg->GetData();
|
|
|
|
EXPECT_EQ(srgData.GetConstant<bool>(srgData.FindShaderInputConstantIndex(Name{ "m_bool" })), true);
|
|
EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), -2);
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 12u);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 1.5f);
|
|
EXPECT_EQ(srgData.GetConstant<Vector2>(srgData.FindShaderInputConstantIndex(Name{ "m_float2" })), Vector2(0.1f, 0.2f));
|
|
// Currently srgData.GetConstant<Vector3> isn't supported so we check the individual floats
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 0), 1.1f);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 1), 1.2f);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 2), 1.3f);
|
|
EXPECT_EQ(srgData.GetConstant<Vector4>(srgData.FindShaderInputConstantIndex(Name{ "m_float4" })), Vector4(2.1f, 2.2f, 2.3f, 2.4f));
|
|
EXPECT_EQ(srgData.GetConstant<Color>(srgData.FindShaderInputConstantIndex(Name{ "m_color" })), TransformColor(Color(1.0f, 1.0f, 1.0f, 1.0f), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg));
|
|
|
|
EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), m_testImage->GetImageView());
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 2u);
|
|
}
|
|
|
|
//! Provides write access to private material asset property values, primarily for simulating
|
|
//! MaterialAsset hot reload.
|
|
MaterialPropertyValue& AccessMaterialAssetPropertyValue(Data::Asset<MaterialAsset> materialAsset, Name propertyName)
|
|
{
|
|
return materialAsset->m_propertyValues[materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyName).GetIndex()];
|
|
}
|
|
};
|
|
|
|
TEST_F(MaterialTests, TestCreateVsFindOrCreate)
|
|
{
|
|
Data::Instance<Material> materialInstance1 = Material::FindOrCreate(m_testMaterialAsset);
|
|
Data::Instance<Material> materialInstance2 = Material::FindOrCreate(m_testMaterialAsset);
|
|
Data::Instance<Material> materialInstance3 = Material::Create(m_testMaterialAsset);
|
|
Data::Instance<Material> materialInstance4 = Material::Create(m_testMaterialAsset);
|
|
|
|
EXPECT_TRUE(materialInstance1);
|
|
EXPECT_TRUE(materialInstance2);
|
|
EXPECT_TRUE(materialInstance3);
|
|
EXPECT_TRUE(materialInstance4);
|
|
|
|
// Instances created via FindOrCreate should be the same object
|
|
|
|
EXPECT_EQ(materialInstance1, materialInstance2);
|
|
EXPECT_NE(materialInstance1, materialInstance3);
|
|
EXPECT_NE(materialInstance1, materialInstance4);
|
|
EXPECT_NE(materialInstance3, materialInstance4);
|
|
}
|
|
|
|
#if AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS
|
|
TEST_F(MaterialTests, DISABLED_TestInitialValuesFromMaterial)
|
|
#else
|
|
TEST_F(MaterialTests, TestInitialValuesFromMaterial)
|
|
#endif // AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
ValidateInitialValuesFromMaterial(material);
|
|
}
|
|
|
|
#if AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS
|
|
TEST_F(MaterialTests, DISABLED_TestSetPropertyValue)
|
|
#else
|
|
TEST_F(MaterialTests, TestSetPropertyValue)
|
|
#endif // AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
Data::Asset<StreamingImageAsset> otherTestImageAsset;
|
|
Data::Instance<StreamingImage> otherTestImage;
|
|
otherTestImageAsset = CreateTestImageAsset();
|
|
otherTestImage = StreamingImage::FindOrCreate(otherTestImageAsset);
|
|
|
|
EXPECT_TRUE(material->SetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" }), false));
|
|
EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" }), -5));
|
|
EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" }), 123u));
|
|
EXPECT_TRUE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f));
|
|
EXPECT_TRUE(material->SetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" }), Vector2(10.1f, 10.2f)));
|
|
EXPECT_TRUE(material->SetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" }), Vector3(11.1f, 11.2f, 11.3f)));
|
|
EXPECT_TRUE(material->SetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" }), Vector4(12.1f, 12.2f, 12.3f, 12.4f)));
|
|
EXPECT_TRUE(material->SetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" }), Color(0.1f, 0.2f, 0.3f, 0.4f)));
|
|
EXPECT_TRUE(material->SetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" }), otherTestImage));
|
|
EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" }), 3u));
|
|
|
|
// Test reading the values directly...
|
|
|
|
EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyBool" })), false);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -5);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyUInt" })), 123u);
|
|
EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 2.5f);
|
|
EXPECT_EQ(material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat2" })), Vector2(10.1f, 10.2f));
|
|
EXPECT_EQ(material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat3" })), Vector3(11.1f, 11.2f, 11.3f));
|
|
EXPECT_EQ(material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
|
|
EXPECT_EQ(material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyColor" })), Color(0.1f, 0.2f, 0.3f, 0.4f));
|
|
EXPECT_EQ(material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyImage" })), otherTestImage);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyEnum" })), 3u);
|
|
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
material->Compile();
|
|
|
|
// Dig in to the SRG to make sure the values were applied there as well...
|
|
|
|
const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
|
|
const RHI::ShaderResourceGroupData& srgData = srg->GetData();
|
|
|
|
EXPECT_EQ(srgData.GetConstant<bool>(srgData.FindShaderInputConstantIndex(Name{ "m_bool" })), false);
|
|
EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), -5);
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 123u);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 2.5f);
|
|
EXPECT_EQ(srgData.GetConstant<Vector2>(srgData.FindShaderInputConstantIndex(Name{ "m_float2" })), Vector2(10.1f, 10.2f));
|
|
// Currently srgData.GetConstant<Vector3> isn't supported so we check the individual floats
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 0), 11.1f);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 1), 11.2f);
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float3" }), 2), 11.3f);
|
|
EXPECT_EQ(srgData.GetConstant<Vector4>(srgData.FindShaderInputConstantIndex(Name{ "m_float4" })), Vector4(12.1f, 12.2f, 12.3f, 12.4f));
|
|
EXPECT_EQ(srgData.GetConstant<Color>(srgData.FindShaderInputConstantIndex(Name{ "m_color" })), TransformColor(Color(0.1f, 0.2f, 0.3f, 0.4f), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg));
|
|
|
|
EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), otherTestImage->GetImageView());
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 3u);
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestSetPropertyValueToMultipleShaderSettings)
|
|
{
|
|
Data::Asset<MaterialTypeAsset> materialTypeAsset;
|
|
Data::Asset<MaterialAsset> materialAsset;
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(m_testMaterialShaderAsset);
|
|
materialTypeCreator.BeginMaterialProperty(Name{ "MyInt" }, MaterialPropertyDataType::Int);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderInput(Name{ "m_int" });
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderInput(Name{ "m_uint" });
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.End(materialTypeAsset);
|
|
|
|
MaterialAssetCreator materialAssetCreator;
|
|
materialAssetCreator.Begin(Uuid::CreateRandom(), *materialTypeAsset);
|
|
materialAssetCreator.End(materialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(materialAsset);
|
|
|
|
EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" }), 42));
|
|
|
|
// Test reading the value directly...
|
|
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), 42);
|
|
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
material->Compile();
|
|
|
|
// Dig in to the SRG to make sure the values were applied to both shader constants...
|
|
|
|
const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
|
|
const RHI::ShaderResourceGroupData& srgData = srg->GetData();
|
|
|
|
EXPECT_EQ(srgData.GetConstant<int32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_int" })), 42);
|
|
EXPECT_EQ(srgData.GetConstant<uint32_t>(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 42u);
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestSetPropertyValueWhenValueIsUnchanged)
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
EXPECT_TRUE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f));
|
|
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
EXPECT_TRUE(material->Compile());
|
|
|
|
// Taint the SRG so we can check whether it was set by the SetPropertyValue() calls below.
|
|
const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup();
|
|
const RHI::ShaderResourceGroupData& srgData = srg->GetData();
|
|
const_cast<RHI::ShaderResourceGroupData*>(&srgData)->SetConstant(m_testMaterialSrgLayout->FindShaderInputConstantIndex(Name{"m_float"}), 0.0f);
|
|
|
|
// Set the properties to the same values as before
|
|
EXPECT_FALSE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f));
|
|
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
EXPECT_FALSE(material->Compile());
|
|
|
|
// Make sure the SRG is still tainted, because the SetPropertyValue() functions weren't processed
|
|
EXPECT_EQ(srgData.GetConstant<float>(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 0.0f);
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestImageNotProvided)
|
|
{
|
|
Data::Asset<MaterialAsset> materialAssetWithEmptyImage;
|
|
|
|
MaterialAssetCreator materialCreator;
|
|
materialCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialCreator.SetPropertyValue(Name{"MyFloat2"}, Vector2{0.1f, 0.2f});
|
|
materialCreator.SetPropertyValue(Name{"MyFloat3"}, Vector3{1.1f, 1.2f, 1.3f});
|
|
materialCreator.SetPropertyValue(Name{"MyFloat4"}, Vector4{2.1f, 2.2f, 2.3f, 2.4f});
|
|
materialCreator.SetPropertyValue(Name{"MyColor"}, Color{1.0f, 1.0f, 1.0f, 1.0f});
|
|
materialCreator.SetPropertyValue(Name{"MyInt"}, -2);
|
|
materialCreator.SetPropertyValue(Name{"MyUInt"}, 12u);
|
|
materialCreator.SetPropertyValue(Name{"MyFloat"}, 1.5f);
|
|
materialCreator.SetPropertyValue(Name{"MyBool"}, true);
|
|
// We don't set "MyImage"
|
|
materialCreator.End(materialAssetWithEmptyImage);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(materialAssetWithEmptyImage);
|
|
|
|
Data::Instance<Image> nullImageInstance;
|
|
Data::Instance<Image> actualImageInstance = material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{"MyImage"}));
|
|
EXPECT_EQ(actualImageInstance, nullImageInstance);
|
|
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
material->Compile();
|
|
|
|
const RHI::ShaderResourceGroupData& srgData = material->GetRHIShaderResourceGroup()->GetData();
|
|
|
|
EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{"m_image"}), 0), nullptr);
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestMaterialWithNoSRGOrProperties)
|
|
{
|
|
// Making a material with no properties and no SRG allows us to create simple shaders
|
|
// that don't need any input, for example a debug shader that just renders surface normals.
|
|
|
|
Data::Asset<MaterialTypeAsset> emptyMaterialTypeAsset;
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
EXPECT_TRUE(materialTypeCreator.End(emptyMaterialTypeAsset));
|
|
|
|
Data::Asset<MaterialAsset> emptyMaterialAsset;
|
|
MaterialAssetCreator materialCreator;
|
|
materialCreator.Begin(Uuid::CreateRandom(), *emptyMaterialTypeAsset);
|
|
EXPECT_TRUE(materialCreator.End(emptyMaterialAsset));
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(emptyMaterialAsset);
|
|
EXPECT_TRUE(material);
|
|
EXPECT_FALSE(material->GetRHIShaderResourceGroup());
|
|
}
|
|
|
|
Ptr<ShaderOptionGroupLayout> CreateTestOptionsLayout()
|
|
{
|
|
AZStd::vector<RPI::ShaderOptionValuePair> enumOptionValues;
|
|
enumOptionValues.push_back({Name("Low"), RPI::ShaderOptionValue(0)});
|
|
enumOptionValues.push_back({Name("Med"), RPI::ShaderOptionValue(1)});
|
|
enumOptionValues.push_back({Name("High"), RPI::ShaderOptionValue(2)});
|
|
|
|
AZStd::vector<RPI::ShaderOptionValuePair> boolOptionValues;
|
|
boolOptionValues.push_back({Name("False"), RPI::ShaderOptionValue(0)});
|
|
boolOptionValues.push_back({Name("True"), RPI::ShaderOptionValue(1)});
|
|
|
|
AZStd::vector<RPI::ShaderOptionValuePair> rangeOptionValues;
|
|
rangeOptionValues.push_back({Name("1"), RPI::ShaderOptionValue(1)});
|
|
rangeOptionValues.push_back({Name("10"), RPI::ShaderOptionValue(10)});
|
|
|
|
Ptr<ShaderOptionGroupLayout> shaderOptions = ShaderOptionGroupLayout::Create();
|
|
uint32_t order = 0;
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enumA"}, ShaderOptionType::Enumeration, 0, order++, enumOptionValues, Name{"Low"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enumB"}, ShaderOptionType::Enumeration, 2, order++, enumOptionValues, Name{"Low"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_enumC"}, ShaderOptionType::Enumeration, 4, order++, enumOptionValues, Name{"Low"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_boolA"}, ShaderOptionType::Boolean, 6, order++, boolOptionValues, Name{"False"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_boolB"}, ShaderOptionType::Boolean, 7, order++, boolOptionValues, Name{"False"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_boolC"}, ShaderOptionType::Boolean, 8, order++, boolOptionValues, Name{"False"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_rangeA"}, ShaderOptionType::IntegerRange, 9, order++, rangeOptionValues, Name{"1"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_rangeB"}, ShaderOptionType::IntegerRange, 13, order++, rangeOptionValues, Name{"1"}});
|
|
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_rangeC"}, ShaderOptionType::IntegerRange, 17, order++, rangeOptionValues, Name{"1"}});
|
|
shaderOptions->Finalize();
|
|
|
|
return shaderOptions;
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToShaderOptions_AllTypes)
|
|
{
|
|
Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
|
|
|
|
Data::Asset<ShaderAsset> shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.BeginMaterialProperty(Name{"EnumA"}, MaterialPropertyDataType::Int);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_enumA"}, 0);
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.BeginMaterialProperty(Name{"EnumB"}, MaterialPropertyDataType::UInt);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_enumB"}, 0);
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.BeginMaterialProperty(Name{"Bool"}, MaterialPropertyDataType::Bool);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_boolA"}, 0);
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.BeginMaterialProperty(Name{"RangeA"}, MaterialPropertyDataType::Int);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_rangeA"}, 0);
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.BeginMaterialProperty(Name{"RangeB"}, MaterialPropertyDataType::UInt);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_rangeB"}, 0);
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.SetPropertyValue(Name{"EnumA"}, 2);
|
|
materialTypeCreator.SetPropertyValue(Name{"EnumB"}, 1u);
|
|
materialTypeCreator.SetPropertyValue(Name{"Bool"}, true);
|
|
materialTypeCreator.SetPropertyValue(Name{"RangeA"}, 5);
|
|
materialTypeCreator.SetPropertyValue(Name{"RangeB"}, 10u);
|
|
materialTypeCreator.End(m_testMaterialTypeAsset);
|
|
|
|
MaterialAssetCreator materialAssetCreator;
|
|
materialAssetCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialAssetCreator.End(m_testMaterialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
auto& optionEnumA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_enumA"}));
|
|
auto& optionEnumB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_enumB"}));
|
|
auto& optionBoolA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_boolA"}));
|
|
auto& optionRangeA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeA"}));
|
|
auto& optionRangeB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeB"}));
|
|
|
|
// Check the values on the properties themselves
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"EnumA"})), 2);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"EnumB"})), 1u);
|
|
EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{"Bool"})), true);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"RangeA"})), 5);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"})), 10u);
|
|
|
|
// Check the values on the underlying ShaderCollection::Item
|
|
ShaderOptionGroup options{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
|
|
EXPECT_EQ(optionEnumA.Get(options).GetIndex(), optionEnumA.FindValue(Name{"High"}).GetIndex());
|
|
EXPECT_EQ(optionEnumB.Get(options).GetIndex(), optionEnumB.FindValue(Name{"Med"}).GetIndex());
|
|
EXPECT_EQ(optionBoolA.Get(options).GetIndex(), optionBoolA.FindValue(Name{"True"}).GetIndex());
|
|
EXPECT_EQ(optionRangeA.Get(options).GetIndex(), 5);
|
|
EXPECT_EQ(optionRangeB.Get(options).GetIndex(), 10);
|
|
|
|
// Now call SetPropertyValue to change the values, and check again
|
|
EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"EnumA"}), 1));
|
|
EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"EnumB"}), 0u));
|
|
EXPECT_TRUE(material->SetPropertyValue<bool>(material->FindPropertyIndex(Name{"Bool"}), false));
|
|
EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"RangeA"}), 3));
|
|
EXPECT_TRUE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"}), 7u));
|
|
|
|
// Check the values on the properties themselves
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"EnumA"})), 1);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"EnumB"})), 0u);
|
|
EXPECT_EQ(material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{"Bool"})), false);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"RangeA"})), 3);
|
|
EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"})), 7u);
|
|
|
|
// Check the values on the underlying ShaderCollection::Item
|
|
ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
|
|
EXPECT_EQ(optionEnumA.Get(options2).GetIndex(), optionEnumA.FindValue(Name{"Med"}).GetIndex());
|
|
EXPECT_EQ(optionEnumB.Get(options2).GetIndex(), optionEnumB.FindValue(Name{"Low"}).GetIndex());
|
|
EXPECT_EQ(optionBoolA.Get(options2).GetIndex(), optionBoolA.FindValue(Name{"False"}).GetIndex());
|
|
EXPECT_EQ(optionRangeA.Get(options2).GetIndex(), 3);
|
|
EXPECT_EQ(optionRangeB.Get(options2).GetIndex(), 7);
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestSetPropertyValue_ConnectedToShaderOptions_WithMultipleShaders)
|
|
{
|
|
Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
|
|
|
|
Data::Asset<ShaderAsset> shaderAsset =
|
|
CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
// Adding more than one shader
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.BeginMaterialProperty(Name{"Value"}, MaterialPropertyDataType::Int);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_rangeC"}, 1);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_rangeA"}, 2);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_rangeB"}); // Applies to all shaders
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.SetPropertyValue(Name{"Value"}, 2);
|
|
materialTypeCreator.End(m_testMaterialTypeAsset);
|
|
|
|
MaterialAssetCreator materialAssetCreator;
|
|
materialAssetCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialAssetCreator.End(m_testMaterialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
auto& optionRangeA = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeA"}));
|
|
auto& optionRangeB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeB"}));
|
|
auto& optionRangeC = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeC"}));
|
|
|
|
// Check the values on the underlying ShaderVariantReferences
|
|
{
|
|
ShaderOptionGroup options0{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
|
|
ShaderOptionGroup options1{optionsLayout, material->GetShaderCollection()[1].GetShaderVariantId()};
|
|
ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection()[2].GetShaderVariantId()};
|
|
EXPECT_EQ(optionRangeA.Get(options0).GetIndex(), -1);
|
|
EXPECT_EQ(optionRangeA.Get(options1).GetIndex(), -1);
|
|
EXPECT_EQ(optionRangeA.Get(options2).GetIndex(), 2);
|
|
EXPECT_EQ(optionRangeB.Get(options0).GetIndex(), 2);
|
|
EXPECT_EQ(optionRangeB.Get(options1).GetIndex(), 2);
|
|
EXPECT_EQ(optionRangeB.Get(options2).GetIndex(), 2);
|
|
EXPECT_EQ(optionRangeC.Get(options0).GetIndex(), -1);
|
|
EXPECT_EQ(optionRangeC.Get(options1).GetIndex(), 2);
|
|
EXPECT_EQ(optionRangeC.Get(options2).GetIndex(), -1);
|
|
}
|
|
|
|
// Now call SetPropertyValue to change the values, and check again
|
|
EXPECT_TRUE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"Value"}), 5));
|
|
|
|
// Check the values on the underlying ShaderVariantReferences
|
|
{
|
|
ShaderOptionGroup options0{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
|
|
ShaderOptionGroup options1{optionsLayout, material->GetShaderCollection()[1].GetShaderVariantId()};
|
|
ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection()[2].GetShaderVariantId()};
|
|
EXPECT_EQ(optionRangeA.Get(options0).GetIndex(), -1);
|
|
EXPECT_EQ(optionRangeA.Get(options1).GetIndex(), -1);
|
|
EXPECT_EQ(optionRangeA.Get(options2).GetIndex(), 5);
|
|
EXPECT_EQ(optionRangeB.Get(options0).GetIndex(), 5);
|
|
EXPECT_EQ(optionRangeB.Get(options1).GetIndex(), 5);
|
|
EXPECT_EQ(optionRangeB.Get(options2).GetIndex(), 5);
|
|
EXPECT_EQ(optionRangeC.Get(options0).GetIndex(), -1);
|
|
EXPECT_EQ(optionRangeC.Get(options1).GetIndex(), 5);
|
|
EXPECT_EQ(optionRangeC.Get(options2).GetIndex(), -1);
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(MaterialTests, TestSetSystemShaderOption)
|
|
{
|
|
Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
|
|
|
|
Data::Asset<ShaderAsset> shaderAsset =
|
|
CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.BeginMaterialProperty(Name{"RangeValue"}, MaterialPropertyDataType::Int);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_rangeA"}, 1);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOption(Name{"o_rangeB"}, 2);
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.BeginMaterialProperty(Name{"BoolValue"}, MaterialPropertyDataType::Bool);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_boolA"}); // Applies to all shaders
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.ClaimShaderOptionOwnership(Name{"o_boolB"});
|
|
materialTypeCreator.SetPropertyValue(Name{"RangeValue"}, 1);
|
|
materialTypeCreator.End(m_testMaterialTypeAsset);
|
|
|
|
MaterialAssetCreator materialAssetCreator;
|
|
materialAssetCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialAssetCreator.End(m_testMaterialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_enumA"}, ShaderOptionValue{0}).GetValue());
|
|
EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_enumB"}, ShaderOptionValue{1}).GetValue());
|
|
EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_enumC"}, ShaderOptionValue{2}).GetValue());
|
|
EXPECT_FALSE(material->SetSystemShaderOption(Name{"o_boolA"}, ShaderOptionValue{1}).IsSuccess());
|
|
EXPECT_FALSE(material->SetSystemShaderOption(Name{"o_boolB"}, ShaderOptionValue{1}).IsSuccess());
|
|
EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_boolC"}, ShaderOptionValue{1}).GetValue());
|
|
EXPECT_FALSE(material->SetSystemShaderOption(Name{"o_rangeA"}, ShaderOptionValue{3}).IsSuccess());
|
|
EXPECT_FALSE(material->SetSystemShaderOption(Name{"o_rangeB"}, ShaderOptionValue{4}).IsSuccess());
|
|
EXPECT_EQ(3, material->SetSystemShaderOption(Name{"o_rangeC"}, ShaderOptionValue{5}).GetValue());
|
|
|
|
// Try setting a shader option that does not exist in this material
|
|
auto result = material->SetSystemShaderOption(Name{"o_someOtherOption"}, ShaderOptionValue{1});
|
|
EXPECT_TRUE(result.IsSuccess());
|
|
EXPECT_EQ(0, result.GetValue());
|
|
|
|
for (size_t i = 0; i < material->GetShaderCollection().size(); ++i)
|
|
{
|
|
auto& shaderItem = material->GetShaderCollection()[i];
|
|
|
|
EXPECT_EQ(0, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumA"}).GetIndex());
|
|
EXPECT_EQ(1, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumB"}).GetIndex());
|
|
EXPECT_EQ(2, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumC"}).GetIndex());
|
|
EXPECT_EQ(1, shaderItem.GetShaderOptions()->GetValue(Name{"o_boolC"}).GetIndex());
|
|
EXPECT_EQ(5, shaderItem.GetShaderOptions()->GetValue(Name{"o_rangeC"}).GetIndex());
|
|
|
|
// We don't care whether a material-owned shader option is unspecified or is initialized to its default state.
|
|
// The important thing is that it did not change from its default value.
|
|
auto checkValueNotChanged = [&shaderItem](const Name& name, ShaderOptionValue expectedValue)
|
|
{
|
|
ShaderOptionValue value = shaderItem.GetShaderOptions()->GetValue(name);
|
|
if (value.IsValid())
|
|
{
|
|
EXPECT_EQ(expectedValue.GetIndex(), value.GetIndex());
|
|
}
|
|
};
|
|
|
|
checkValueNotChanged(Name{"o_boolA"}, ShaderOptionValue{0});
|
|
checkValueNotChanged(Name{"o_boolB"}, ShaderOptionValue{0});
|
|
checkValueNotChanged(Name{"o_rangeA"}, ShaderOptionValue{1});
|
|
checkValueNotChanged(Name{"o_rangeB"}, ShaderOptionValue{1});
|
|
}
|
|
}
|
|
|
|
TEST_F(MaterialTests, Error_InvalidShaderOptionValue)
|
|
{
|
|
Ptr<ShaderOptionGroupLayout> optionsLayout = CreateTestOptionsLayout();
|
|
|
|
Data::Asset<ShaderAsset> shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout, optionsLayout);
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.BeginMaterialProperty(Name{"Value"}, MaterialPropertyDataType::Int);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderOptions(Name{"o_rangeA"});
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.SetPropertyValue(Name{"Value"}, 1);
|
|
materialTypeCreator.End(m_testMaterialTypeAsset);
|
|
|
|
MaterialAssetCreator materialAssetCreator;
|
|
materialAssetCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialAssetCreator.End(m_testMaterialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
AZ_TEST_START_ASSERTTEST;
|
|
EXPECT_FALSE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"Value"}), 100));
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
}
|
|
|
|
TEST_F(MaterialTests, Error_ImageNotFound)
|
|
{
|
|
Data::Asset<MaterialAsset> materialAsset;
|
|
MaterialAssetCreator materialCreator;
|
|
materialCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat2" }, Vector2{ 0.1f, 0.2f });
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat3" }, Vector3{ 1.1f, 1.2f, 1.3f });
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat4" }, Vector4{ 2.1f, 2.2f, 2.3f, 2.4f });
|
|
materialCreator.SetPropertyValue(Name{ "MyColor" }, Color{ 1.0f, 1.0f, 1.0f, 1.0f });
|
|
materialCreator.SetPropertyValue(Name{ "MyInt" }, -2);
|
|
materialCreator.SetPropertyValue(Name{ "MyUInt" }, 12u);
|
|
materialCreator.SetPropertyValue(Name{ "MyFloat" }, 1.5f);
|
|
materialCreator.SetPropertyValue(Name{ "MyBool" }, true);
|
|
// Set the image to an empty asset handle that isn't associated with any actual data. StreamingImage::FindOrCreate will fail.
|
|
materialCreator.SetPropertyValue(Name{ "MyImage" }, Data::Asset<ImageAsset>(Uuid::CreateRandom(), azrtti_typeid<StreamingImageAsset>()));
|
|
materialCreator.End(materialAsset);
|
|
|
|
ErrorMessageFinder errorMessageFinder{"Image asset could not be loaded"};
|
|
// The material may trigger a blocking load of the image asset, but there is no catalog in unit tests.
|
|
errorMessageFinder.AddIgnoredErrorMessage("this type doesn't have a catalog", true);
|
|
errorMessageFinder.AddIgnoredErrorMessage("Failed to retrieve required information for asset", true);
|
|
errorMessageFinder.AddIgnoredErrorMessage("GetAsset called for asset which does not exist", true);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(materialAsset);
|
|
|
|
errorMessageFinder.CheckExpectedErrorsFound();
|
|
|
|
EXPECT_EQ(nullptr, material);
|
|
}
|
|
|
|
TEST_F(MaterialTests, Error_AccessInvalidProperty)
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
AZ_TEST_START_ASSERTTEST;
|
|
EXPECT_FALSE(material->SetPropertyValue<float>(MaterialPropertyIndex(), 0.0f));
|
|
material->GetPropertyValue<float>(MaterialPropertyIndex());
|
|
AZ_TEST_STOP_ASSERTTEST(2);
|
|
}
|
|
|
|
#if AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS
|
|
TEST_F(MaterialTests, DISABLED_Error_SetPropertyValue_WrongDataType)
|
|
#else
|
|
TEST_F(MaterialTests, Error_SetPropertyValue_WrongDataType)
|
|
#endif // AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
{
|
|
ErrorMessageFinder finder;
|
|
finder.AddExpectedErrorMessage("Accessed as type", 9);
|
|
finder.AddExpectedErrorMessage("but is type", 9);
|
|
|
|
EXPECT_FALSE(material->SetPropertyValue<bool>(material->FindPropertyIndex(Name{"MyImage"}), false));
|
|
EXPECT_FALSE(material->SetPropertyValue<int32_t>(material->FindPropertyIndex(Name{"MyBool"}), -5));
|
|
EXPECT_FALSE(material->SetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"MyInt"}), 123u));
|
|
EXPECT_FALSE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{"MyUInt"}), 2.5f));
|
|
EXPECT_FALSE(material->SetPropertyValue<Vector2>(material->FindPropertyIndex(Name{"MyFloat"}), Vector2(10.1f, 10.2f)));
|
|
EXPECT_FALSE(material->SetPropertyValue<Vector3>(material->FindPropertyIndex(Name{"MyFloat2"}), Vector3(11.1f, 11.2f, 11.3f)));
|
|
EXPECT_FALSE(material->SetPropertyValue<Vector4>(material->FindPropertyIndex(Name{"MyFloat3"}), Vector4(12.1f, 12.2f, 12.3f, 12.4f)));
|
|
EXPECT_FALSE(material->SetPropertyValue<Color>(material->FindPropertyIndex(Name{"MyFloat4"}), Color(0.1f, 0.2f, 0.3f, 0.4f)));
|
|
EXPECT_FALSE(material->SetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{"MyColor"}), m_testImage));
|
|
|
|
finder.CheckExpectedErrorsFound();
|
|
}
|
|
|
|
// Make sure the values have not changed
|
|
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
material->Compile();
|
|
|
|
ValidateInitialValuesFromMaterial(material);
|
|
}
|
|
|
|
TEST_F(MaterialTests, Error_GetPropertyValue_WrongDataType)
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
ErrorMessageFinder finder;
|
|
finder.AddExpectedErrorMessage("Accessed as type", 9);
|
|
finder.AddExpectedErrorMessage("but is type", 9);
|
|
|
|
material->GetPropertyValue<bool>(material->FindPropertyIndex(Name{ "MyImage" }));
|
|
material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyBool" }));
|
|
material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{ "MyInt" }));
|
|
material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyUInt" }));
|
|
material->GetPropertyValue<Vector2>(material->FindPropertyIndex(Name{ "MyFloat" }));
|
|
material->GetPropertyValue<Vector3>(material->FindPropertyIndex(Name{ "MyFloat2" }));
|
|
material->GetPropertyValue<Vector4>(material->FindPropertyIndex(Name{ "MyFloat3" }));
|
|
material->GetPropertyValue<Color>(material->FindPropertyIndex(Name{ "MyFloat4" }));
|
|
material->GetPropertyValue<Data::Instance<Image>>(material->FindPropertyIndex(Name{ "MyColor" }));
|
|
|
|
finder.CheckExpectedErrorsFound();
|
|
}
|
|
|
|
TEST_F(MaterialTests, ColorPropertyCanMapToFloat3)
|
|
{
|
|
Data::Asset<MaterialTypeAsset> materialTypeAsset;
|
|
Data::Asset<MaterialAsset> materialAsset;
|
|
|
|
RHI::Ptr<RHI::ShaderResourceGroupLayout> srgLayout = RHI::ShaderResourceGroupLayout::Create();
|
|
srgLayout->SetName(Name("MaterialSrg"));
|
|
srgLayout->SetBindingSlot(SrgBindingSlot::Material);
|
|
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name{"m_color"}, 0, 12, 0});
|
|
ASSERT_TRUE(srgLayout->Finalize());
|
|
|
|
Data::Asset<ShaderAsset> shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), srgLayout);
|
|
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(shaderAsset);
|
|
materialTypeCreator.BeginMaterialProperty(Name{ "MyColor" }, MaterialPropertyDataType::Color);
|
|
materialTypeCreator.ConnectMaterialPropertyToShaderInput(Name{ "m_color" });
|
|
materialTypeCreator.EndMaterialProperty();
|
|
materialTypeCreator.End(materialTypeAsset);
|
|
|
|
MaterialAssetCreator materialAssetCreator;
|
|
materialAssetCreator.Begin(Uuid::CreateRandom(), *materialTypeAsset);
|
|
materialAssetCreator.End(materialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(materialAsset);
|
|
|
|
const AZ::Color inputColor{1.0f, 2.0f, 3.0f, 0.0f};
|
|
|
|
MaterialPropertyIndex colorProperty{0};
|
|
material->SetPropertyValue(colorProperty, inputColor);
|
|
|
|
ProcessQueuedSrgCompilations(shaderAsset, srgLayout->GetName());
|
|
material->Compile();
|
|
|
|
AZ::Color colorFromMaterial = material->GetPropertyValue<AZ::Color>(colorProperty);
|
|
|
|
RHI::ShaderInputConstantIndex colorConstant{0};
|
|
AZ::Color colorFromSrg;
|
|
const float* floatsFromSrg = reinterpret_cast<const float*>(material->GetRHIShaderResourceGroup()->GetData().GetConstantRaw(colorConstant).data());
|
|
colorFromSrg = AZ::Color::CreateFromVector3(AZ::Vector3::CreateFromFloat3(floatsFromSrg));
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
EXPECT_EQ((float)TransformColor(inputColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg).GetElement(i), (float)colorFromSrg.GetElement(i));
|
|
EXPECT_EQ((float)inputColor.GetElement(i), (float)colorFromMaterial.GetElement(i));
|
|
}
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestReinitializeForHotReload)
|
|
{
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
const RHI::ShaderResourceGroupData* srgData = &material->GetRHIShaderResourceGroup()->GetData();
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
|
|
// Check the default property value
|
|
EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 1.5f);
|
|
EXPECT_EQ(srgData->GetConstant<float>(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 1.5f);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -2);
|
|
EXPECT_EQ(srgData->GetConstant<int32_t>(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -2);
|
|
|
|
// Override a property value
|
|
EXPECT_TRUE(material->SetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" }), 5.5f));
|
|
|
|
// Apply the changes
|
|
EXPECT_TRUE(material->Compile());
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
|
|
// Check the updated values with one overridden
|
|
EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 5.5f);
|
|
EXPECT_EQ(srgData->GetConstant<float>(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 5.5f);
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -2);
|
|
EXPECT_EQ(srgData->GetConstant<int32_t>(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -2);
|
|
|
|
// Pretend there was a hot-reload with new default values
|
|
AccessMaterialAssetPropertyValue(m_testMaterialAsset, Name{"MyFloat"}) = 0.5f;
|
|
AccessMaterialAssetPropertyValue(m_testMaterialAsset, Name{"MyInt"}) = -7;
|
|
AZ::Data::AssetBus::Event(m_testMaterialAsset.GetId(), &AZ::Data::AssetBus::Handler::OnAssetReloaded, m_testMaterialAsset);
|
|
srgData = &material->GetRHIShaderResourceGroup()->GetData();
|
|
ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName());
|
|
|
|
// Make sure the override values are still there
|
|
EXPECT_EQ(srgData->GetConstant<float>(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 5.5f);
|
|
EXPECT_EQ(material->GetPropertyValue<float>(material->FindPropertyIndex(Name{ "MyFloat" })), 5.5f);
|
|
|
|
// Make sure the new default value is applied where it was not overridden
|
|
EXPECT_EQ(material->GetPropertyValue<int32_t>(material->FindPropertyIndex(Name{ "MyInt" })), -7);
|
|
EXPECT_EQ(srgData->GetConstant<int32_t>(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -7);
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestFindPropertyIndexUsingOldName)
|
|
{
|
|
MaterialTypeAssetCreator materialTypeCreator;
|
|
materialTypeCreator.Begin(Uuid::CreateRandom());
|
|
materialTypeCreator.AddShader(m_testMaterialShaderAsset);
|
|
AddCommonTestMaterialProperties(materialTypeCreator);
|
|
materialTypeCreator.SetVersion(2);
|
|
MaterialVersionUpdate versionUpdate(2);
|
|
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "OldName" },Name{ "MyInt" }}));
|
|
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
|
materialTypeCreator.End(m_testMaterialTypeAsset);
|
|
|
|
MaterialAssetCreator materialCreator;
|
|
materialCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
|
|
materialCreator.End(m_testMaterialAsset);
|
|
|
|
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
|
|
|
|
bool wasRenamed = false;
|
|
Name newName;
|
|
MaterialPropertyIndex indexFromOldName = material->FindPropertyIndex(Name{"OldName"}, &wasRenamed, &newName);
|
|
EXPECT_TRUE(wasRenamed);
|
|
EXPECT_EQ(newName, Name{"MyInt"});
|
|
|
|
MaterialPropertyIndex indexFromNewName = material->FindPropertyIndex(Name{"MyInt"}, &wasRenamed, &newName);
|
|
EXPECT_FALSE(wasRenamed);
|
|
|
|
EXPECT_EQ(indexFromOldName, indexFromNewName);
|
|
}
|
|
|
|
}
|