Files
o3de/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp
T
galibzon cc615a8f32 [ATOM-15472] Shader Build Pipeline: Remove Deprecated Files And Funct… (#1079)
* [ATOM-15472] Shader Build Pipeline: Remove Deprecated Files And Functions That
Predate The Shader Supervariants

These are the essential impactful changes as a result of deprecating the
ShaderResourceGroupAsset.

* Addressed feedback by @moudgils. Better comments in header files.

* More updates related with deprecation of ShaderResourceGroupAsset

* Deleted the temporary version 2 classes.

* Updated version of the shader asset builders.

* Updated version of all the shader related classes impacted
by the Supervariant concept and deprecation of ShaderResourceGroupAsset

* Changes to *.pass and DGI, Reflections and RayTracing.

* changes to material related assets

* changes to core lights

* Changes to auxgeom/dynamic draw.

* changes to decals, lyshine, imguipass

* changes to RPI Pass classes

* Shader for SceneSrg, ViewSrg and ForwardPass Srgs.

* changes to mesh, skinned mesh, Morphtarget.

* Fixes to RayTracingPass.cpp & now allow empty srg in shaders.

* Updated Atom_RPI.Tests

* Simplified InstanceDatabase by removing AddHandler

------------------------------------------------------------------------------------
* Updated DiffuseGI precompiled shaders.
Added RayTracingSceneSrg and RayTracingMaterialSrg shader asset.
Updated ShaderAssetCreator::Clone to handle the supervariant when processing root variants.
Co-authored-by: Doug McDiarmid <dmcdiar@amazon.com>
------------------------------------------------------------------------------------

* Changed semantics for some PassSrg to SRG_PerPass_WithFallback.

AuxGeom/FixedShapeProcessor.cpp requires SRG_PerDraw on ObjectSrg.

Removed names of SceneSrg and ViewSrg from RPISystemDescriptor.cpp

* Moved ShaderLib/Atom/Features/DummyEntryFunctions.azsli
To  Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/DummyEntryFunctions.azsli

Removed redundant checking for finalization in
ShaderResourceGroupLayout.cpp

* Fixed race condition bug for Shader::FindOrCreate.
InstanceDatabase<>::CreateInstance() needs to be atomic
for instance creation and initialization.

Added optional InstanceHandler::CreateFunctionWithParams to accomodate
to the needs of Instances that need more than an asset reference
to be able to be created an initialzed.

Removed ShaderResourceGroup::FindOrCreate() only ::Create is available
now.

* Renamed scene_and_view_srgs.* as SceneAndViewSrgs.*

Changed GetAzslFileOfOrigin for GetUniqueId

* Fixed unit tests.

* Reverted the serialization name of m_uniqueId back to
"m_azslFileOfOrigin" so precompiled shaders don't fail
in layout comparison.

* Fixed AtomCore.Tests
Removed non-applicable test. InstanceDatabase.AddHandler() is not
available anymore.

* The Null rhi is re-enabled for shader compilation.

Signed-off-by: garrieta <garrieta@amazon.com>
2021-06-11 07:48:23 -05:00

315 lines
15 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzTest/AzTest.h>
#include <Common/RPITestFixture.h>
#include <Common/JsonTestUtils.h>
#include <Common/ErrorMessageFinder.h>
#include <Common/ShaderAssetTestUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h>
#include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
#include <Atom/RPI.Edit/Material/MaterialFunctorSourceData.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Material/MaterialAssetTestUtils.h>
//#include <AtomCore/Serialization/Json/JsonUtils.h>
namespace UnitTest
{
using namespace AZ;
using namespace RPI;
class MaterialFunctorTests
: public RPITestFixture
{
public:
class SetShaderOptionFunctor final
: public MaterialFunctor
{
public:
AZ_RTTI(SetShaderOptionFunctor, "{6316D98D-D2DD-4E9C-808C-58118DC9FF73}", MaterialFunctor);
SetShaderOptionFunctor(size_t shaderIndex, ShaderOptionIndex shaderOptionIndex, ShaderOptionValue shaderOptionValue)
: m_shaderIndex(shaderIndex)
, m_shaderOptionIndex(shaderOptionIndex)
, m_shaderOptionValue(shaderOptionValue)
{
}
void Process(MaterialFunctor::RuntimeContext& context) override
{
m_processResult = context.SetShaderOptionValue(0, m_shaderOptionIndex, m_shaderOptionValue);
}
// Note a real functor wouldn't do this, it's just for testing
bool GetProcessResult()
{
return m_processResult;
}
private:
size_t m_shaderIndex;
ShaderOptionIndex m_shaderOptionIndex;
ShaderOptionValue m_shaderOptionValue;
bool m_processResult = false;
};
class PropertyDependencyTestFunctor final
: public MaterialFunctor
{
public:
MOCK_METHOD0(ProcessCalled, void());
void Process(RuntimeContext& context) override
{
ProcessCalled();
context.GetMaterialPropertyValue<int32_t>(m_registedPropertyIndex);
context.GetMaterialPropertyValue<int32_t>(m_registedPropertyName);
// Should report error in the call.
context.GetMaterialPropertyValue<int32_t>(m_unregistedPropertyIndex);
context.GetMaterialPropertyValue<int32_t>(m_unregistedPropertyName);
}
MaterialPropertyIndex m_registedPropertyIndex;
MaterialPropertyIndex m_unregistedPropertyIndex;
AZ::Name m_registedPropertyName;
AZ::Name m_unregistedPropertyName;
};
class PropertyDependencyTestFunctorSourceData final
: public MaterialFunctorSourceData
{
public:
FunctorResult CreateFunctor(const RuntimeContext& context) const override
{
Ptr<PropertyDependencyTestFunctor> functor = aznew PropertyDependencyTestFunctor;
functor->m_registedPropertyIndex = context.FindMaterialPropertyIndex(Name{ m_registedPropertyName });
EXPECT_TRUE(!functor->m_registedPropertyIndex.IsNull());
AddMaterialPropertyDependency(functor, functor->m_registedPropertyIndex);
functor->m_unregistedPropertyIndex = context.FindMaterialPropertyIndex(Name{ m_unregistedPropertyName });
EXPECT_TRUE(!functor->m_unregistedPropertyIndex.IsNull());
// Intended missing registration to m_materialPropertyDependencies
functor->m_registedPropertyName = m_registedPropertyName;
functor->m_unregistedPropertyName = m_unregistedPropertyName;
return Success(Ptr<MaterialFunctor>(functor));
}
AZStd::string m_registedPropertyName;
AZStd::string m_unregistedPropertyName;
};
protected:
void SetUp() override
{
RPITestFixture::SetUp();
}
void TearDown() override
{
RPITestFixture::TearDown();
}
};
TEST_F(MaterialFunctorTests, MaterialFunctor_RuntimeContext_ShaderOptionNotOwned)
{
using namespace AZ::RPI;
AZStd::vector<RPI::ShaderOptionValuePair> boolOptionValues;
boolOptionValues.push_back({Name("False"), RPI::ShaderOptionValue(0)});
boolOptionValues.push_back({Name("True"), RPI::ShaderOptionValue(1)});
AZ::RPI::Ptr<AZ::RPI::ShaderOptionGroupLayout> shaderOptions = RPI::ShaderOptionGroupLayout::Create();
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_optionA"}, ShaderOptionType::Boolean, 0, 0, boolOptionValues, Name{"False"}});
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_optionB"}, ShaderOptionType::Boolean, 1, 1, boolOptionValues, Name{"False"}});
shaderOptions->AddShaderOption(ShaderOptionDescriptor{Name{"o_optionC"}, ShaderOptionType::Boolean, 2, 2, boolOptionValues, Name{"False"}});
shaderOptions->Finalize();
Data::Asset<MaterialTypeAsset> materialTypeAsset;
//Data::Asset<MaterialAsset> materialAsset;
// Note we don't actually need any properties or functors in the material type. We just need to set up some sample data
// structures that we can pass to the functors below, especially the shader with shader options.
MaterialTypeAssetCreator materialTypeCreator;
materialTypeCreator.Begin(Uuid::CreateRandom());
materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom(), CreateCommonTestMaterialSrgLayout(), shaderOptions));
// We claim ownership of options A and B, but not C. So C is a globally accessible option, not owned by the material.
materialTypeCreator.ClaimShaderOptionOwnership(Name{"o_optionA"});
materialTypeCreator.ClaimShaderOptionOwnership(Name{"o_optionB"});
EXPECT_TRUE(materialTypeCreator.End(materialTypeAsset));
SetShaderOptionFunctor testFunctorSetOptionA{0, ShaderOptionIndex{0}, ShaderOptionValue{1}};
SetShaderOptionFunctor testFunctorSetOptionB{0, ShaderOptionIndex{1}, ShaderOptionValue{1}};
SetShaderOptionFunctor testFunctorSetOptionC{0, ShaderOptionIndex{2}, ShaderOptionValue{1}};
SetShaderOptionFunctor testFunctorSetOptionInvalid{0, ShaderOptionIndex{3}, ShaderOptionValue{1}};
// Most of this data can be empty since this particular functor doesn't access it.
AZStd::vector<MaterialPropertyValue> unusedPropertyValues;
ShaderResourceGroup* unusedSrg = nullptr;
ShaderCollection shaderCollectionCopy = materialTypeAsset->GetShaderCollection();
{
// Successfully set o_optionA
MaterialFunctor::RuntimeContext runtimeContext = MaterialFunctor::RuntimeContext{
unusedPropertyValues,
materialTypeAsset->GetMaterialPropertiesLayout(),
&shaderCollectionCopy,
unusedSrg,
&testFunctorSetOptionA.GetMaterialPropertyDependencies()
};
testFunctorSetOptionA.Process(runtimeContext);
EXPECT_TRUE(testFunctorSetOptionA.GetProcessResult());
EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 0 }).GetIndex());
EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 1 }).GetIndex());
EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 2 }).GetIndex());
}
{
// Successfully set o_optionB
MaterialFunctor::RuntimeContext runtimeContext = MaterialFunctor::RuntimeContext{
unusedPropertyValues,
materialTypeAsset->GetMaterialPropertiesLayout(),
&shaderCollectionCopy,
unusedSrg,
&testFunctorSetOptionB.GetMaterialPropertyDependencies()
};
testFunctorSetOptionB.Process(runtimeContext);
EXPECT_TRUE(testFunctorSetOptionB.GetProcessResult());
EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 0 }).GetIndex());
EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 1 }).GetIndex());
EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 2 }).GetIndex());
}
{
// Fail to set o_optionC because it is not owned by the material type
AZ_TEST_START_TRACE_SUPPRESSION;
MaterialFunctor::RuntimeContext runtimeContext = MaterialFunctor::RuntimeContext{
unusedPropertyValues,
materialTypeAsset->GetMaterialPropertiesLayout(),
&shaderCollectionCopy,
unusedSrg,
&testFunctorSetOptionC.GetMaterialPropertyDependencies()
};
testFunctorSetOptionC.Process(runtimeContext);
EXPECT_FALSE(testFunctorSetOptionC.GetProcessResult());
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
{
// Fail to set option index that is out of range
AZ_TEST_START_TRACE_SUPPRESSION;
MaterialFunctor::RuntimeContext runtimeContext = MaterialFunctor::RuntimeContext{
unusedPropertyValues,
materialTypeAsset->GetMaterialPropertiesLayout(),
&shaderCollectionCopy,
unusedSrg,
&testFunctorSetOptionInvalid.GetMaterialPropertyDependencies()
};
testFunctorSetOptionInvalid.Process(runtimeContext);
EXPECT_FALSE(testFunctorSetOptionInvalid.GetProcessResult());
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{0}).GetIndex());
EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{1}).GetIndex());
EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{2}).GetIndex());
}
TEST_F(MaterialFunctorTests, ReprocessTest)
{
Data::Asset<MaterialTypeAsset> m_testMaterialTypeAsset;
Data::Asset<MaterialAsset> m_testMaterialAsset;
AZ::Name registedPropertyName("PropA");
AZ::Name unregistedPropertyName("PropB");
AZ::Name unrelatedPropertyName("PropC");
MaterialTypeAssetCreator materialTypeCreator;
materialTypeCreator.Begin(Uuid::CreateRandom());
materialTypeCreator.BeginMaterialProperty(registedPropertyName, AZ::RPI::MaterialPropertyDataType::Int);
materialTypeCreator.EndMaterialProperty();
materialTypeCreator.BeginMaterialProperty(unregistedPropertyName, AZ::RPI::MaterialPropertyDataType::Int);
materialTypeCreator.EndMaterialProperty();
materialTypeCreator.BeginMaterialProperty(unrelatedPropertyName, AZ::RPI::MaterialPropertyDataType::Int);
materialTypeCreator.EndMaterialProperty();
materialTypeCreator.SetPropertyValue(registedPropertyName, 42);
materialTypeCreator.SetPropertyValue(unregistedPropertyName, 42);
materialTypeCreator.SetPropertyValue(unrelatedPropertyName, 42);
PropertyDependencyTestFunctorSourceData functorSourceData;
functorSourceData.m_registedPropertyName = registedPropertyName.GetStringView();
functorSourceData.m_unregistedPropertyName = unregistedPropertyName.GetStringView();
MaterialFunctorSourceData::FunctorResult result = functorSourceData.CreateFunctor(
MaterialFunctorSourceData::RuntimeContext(
"Dummy.materialtype",
materialTypeCreator.GetMaterialPropertiesLayout(),
materialTypeCreator.GetMaterialShaderResourceGroupLayout(),
materialTypeCreator.GetShaderCollection()
)
);
EXPECT_TRUE(result.IsSuccess());
Ptr<MaterialFunctor>& functor = result.GetValue();
EXPECT_TRUE(functor != nullptr);
materialTypeCreator.AddMaterialFunctor(functor);
materialTypeCreator.End(m_testMaterialTypeAsset);
MaterialAssetCreator materialCreator;
materialCreator.Begin(Uuid::CreateRandom(), *m_testMaterialTypeAsset);
materialCreator.SetPropertyValue(registedPropertyName, 42);
materialCreator.SetPropertyValue(unregistedPropertyName, 42);
materialCreator.SetPropertyValue(unrelatedPropertyName, 42);
materialCreator.End(m_testMaterialAsset);
EXPECT_TRUE(m_testMaterialAsset->GetMaterialFunctors().size() == 1u);
PropertyDependencyTestFunctor* testFunctor = static_cast<PropertyDependencyTestFunctor*>(m_testMaterialAsset->GetMaterialFunctors()[0].get());
ErrorMessageFinder errorMessageFinder;
// Expect creation will call functor process once.
EXPECT_CALL(*testFunctor, ProcessCalled()).Times(1);
// Suppress 1 error as we know an unregistered dependent property will be accessed.
errorMessageFinder.Reset();
errorMessageFinder.AddExpectedErrorMessage("Material functor accessing an unregistered material property", 2);
Data::Instance<Material> material = Material::FindOrCreate(m_testMaterialAsset);
errorMessageFinder.CheckExpectedErrorsFound();
material->SetPropertyValue(material->FindPropertyIndex(registedPropertyName), int32_t(24));
// Expect dependent property change will call functor process once.
EXPECT_CALL(*testFunctor, ProcessCalled()).Times(1);
// Suppress 1 error as we know an unregistered dependent property will be accessed.
errorMessageFinder.Reset();
errorMessageFinder.AddExpectedErrorMessage("Material functor accessing an unregistered material property", 2);
material->Compile();
errorMessageFinder.CheckExpectedErrorsFound();
// Expect unrelated property change won't call functor process.
material->SetPropertyValue(material->FindPropertyIndex(unrelatedPropertyName), int32_t(24));
EXPECT_CALL(*testFunctor, ProcessCalled()).Times(0);
material->Compile();
m_testMaterialTypeAsset = {};
m_testMaterialAsset = {};
}
}