You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
931 lines
56 KiB
C++
931 lines
56 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);
|
|
}
|
|
|
|
template<typename T>
|
|
void CheckPropertyValueRoundTrip(const T& value)
|
|
{
|
|
AZ::RPI::MaterialPropertyValue materialPropertyValue{value};
|
|
AZStd::any anyValue{value};
|
|
AZ::RPI::MaterialPropertyValue materialPropertyValueFromAny = MaterialPropertyValue::FromAny(anyValue);
|
|
AZ::RPI::MaterialPropertyValue materialPropertyValueFromRoundTrip = MaterialPropertyValue::FromAny(MaterialPropertyValue::ToAny(materialPropertyValue));
|
|
|
|
EXPECT_EQ(materialPropertyValue, materialPropertyValueFromAny);
|
|
EXPECT_EQ(materialPropertyValue, materialPropertyValueFromRoundTrip);
|
|
|
|
if (materialPropertyValue.Is<Data::Asset<ImageAsset>>())
|
|
{
|
|
EXPECT_EQ(materialPropertyValue.GetValue<Data::Asset<ImageAsset>>().GetHint(), materialPropertyValueFromAny.GetValue<Data::Asset<ImageAsset>>().GetHint());
|
|
EXPECT_EQ(materialPropertyValue.GetValue<Data::Asset<ImageAsset>>().GetHint(), materialPropertyValueFromRoundTrip.GetValue<Data::Asset<ImageAsset>>().GetHint());
|
|
}
|
|
}
|
|
|
|
TEST_F(MaterialTests, TestMaterialPropertyValueAsAny)
|
|
{
|
|
CheckPropertyValueRoundTrip(true);
|
|
CheckPropertyValueRoundTrip(false);
|
|
CheckPropertyValueRoundTrip(7);
|
|
CheckPropertyValueRoundTrip(8u);
|
|
CheckPropertyValueRoundTrip(9.0f);
|
|
CheckPropertyValueRoundTrip(AZ::Vector2(1.0f, 2.0f));
|
|
CheckPropertyValueRoundTrip(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
|
CheckPropertyValueRoundTrip(AZ::Vector4(1.0f, 2.0f, 3.0f, 4.0f));
|
|
CheckPropertyValueRoundTrip(AZ::Color(1.0f, 2.0f, 3.0f, 4.0f));
|
|
CheckPropertyValueRoundTrip(Data::Asset<Data::AssetData>{});
|
|
CheckPropertyValueRoundTrip(Data::Asset<ImageAsset>{});
|
|
CheckPropertyValueRoundTrip(Data::Asset<StreamingImageAsset>{});
|
|
CheckPropertyValueRoundTrip(Data::Asset<Data::AssetData>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), "TestAssetPath.png"});
|
|
CheckPropertyValueRoundTrip(Data::Asset<ImageAsset>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), "TestAssetPath.png"});
|
|
CheckPropertyValueRoundTrip(Data::Asset<StreamingImageAsset>{Uuid::CreateRandom(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), "TestAssetPath.png"});
|
|
CheckPropertyValueRoundTrip(m_testImageAsset);
|
|
CheckPropertyValueRoundTrip(Data::Instance<Image>{m_testImage});
|
|
CheckPropertyValueRoundTrip(AZStd::string{"hello"});
|
|
}
|
|
}
|