| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- /*
- * 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 <Atom/RPI.Public/ColorManagement/TransformColor.h>
- #include <Atom/RPI.Public/Material/Material.h>
- #include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
- #include <Atom/RPI.Public/Image/AttachmentImage.h>
- #include <Atom/RPI.Public/Image/StreamingImage.h>
- #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
- #include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
- #include <Atom/RPI.Public/Shader/Shader.h>
- #include <Atom/RPI.Public/Shader/ShaderSystemInterface.h>
- #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
- #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
- #include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
- #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
- #include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
- #include <AtomCore/Instance/InstanceDatabase.h>
- #include <AtomCore/Utils/ScopedValue.h>
- namespace AZ
- {
- namespace RPI
- {
- const char* Material::s_debugTraceName = "Material";
- Data::Instance<Material> Material::FindOrCreate(const Data::Asset<MaterialAsset>& materialAsset)
- {
- return Data::InstanceDatabase<Material>::Instance().FindOrCreate(materialAsset);
- }
- Data::Instance<Material> Material::Create(const Data::Asset<MaterialAsset>& materialAsset)
- {
- return Data::InstanceDatabase<Material>::Instance().Create(materialAsset);
- }
- AZ::Data::Instance<Material> Material::CreateInternal(MaterialAsset& materialAsset)
- {
- Data::Instance<Material> material = aznew Material();
- const RHI::ResultCode resultCode = material->Init(materialAsset);
- if (resultCode == RHI::ResultCode::Success)
- {
- return material;
- }
- return nullptr;
- }
- RHI::ResultCode Material::Init(MaterialAsset& materialAsset)
- {
- AZ_PROFILE_FUNCTION(RPI);
- ScopedValue isInitializing(&m_isInitializing, true, false);
- // All of these members must be reset if the material can be reinitialized because of the shader reload notification bus
- m_shaderResourceGroup = {};
- m_rhiShaderResourceGroup = {};
- m_materialProperties = {};
- m_generalShaderCollection = {};
- m_materialPipelineData = {};
- m_materialAsset = { &materialAsset, AZ::Data::AssetLoadBehavior::PreLoad };
- ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
- if (!m_materialAsset.IsReady())
- {
- AZ_Error(s_debugTraceName, false, "Material::Init failed because shader asset is not ready. materialAsset uuid=%s",
- materialAsset.GetId().ToFixedString().c_str());
- return RHI::ResultCode::Fail;
- }
- if (!m_materialAsset->InitializeNonSerializedData())
- {
- AZ_Error(s_debugTraceName, false, "Material::InitializeNonSerializedData is not supposed to fail. materialAsset uuid=%s",
- materialAsset.GetId().ToFixedString().c_str());
- return RHI::ResultCode::Fail;
- }
- // Cache off pointers to some key data structures from the material type...
- auto srgLayout = m_materialAsset->GetMaterialSrgLayout();
- if (srgLayout)
- {
- auto shaderAsset = m_materialAsset->GetMaterialTypeAsset()->GetShaderAssetForMaterialSrg();
- m_shaderResourceGroup = ShaderResourceGroup::Create(shaderAsset, srgLayout->GetName());
- if (m_shaderResourceGroup)
- {
- m_rhiShaderResourceGroup = m_shaderResourceGroup->GetRHIShaderResourceGroup();
- }
- else
- {
- // No need to report an error message here, ShaderResourceGroup::Create() will have reported.
- return RHI::ResultCode::Fail;
- }
- }
- m_generalShaderCollection = m_materialAsset->GetGeneralShaderCollection();
- if (!m_materialProperties.Init(m_materialAsset->GetMaterialPropertiesLayout(), m_materialAsset->GetPropertyValues()))
- {
- return RHI::ResultCode::Fail;
- }
- m_materialProperties.SetAllPropertyDirtyFlags();
- for (auto& [materialPipelineName, materialPipeline] : m_materialAsset->GetMaterialPipelinePayloads())
- {
- MaterialPipelineState& pipelineData = m_materialPipelineData[materialPipelineName];
- pipelineData.m_shaderCollection = materialPipeline.m_shaderCollection;
- if (!pipelineData.m_materialProperties.Init(materialPipeline.m_materialPropertiesLayout, materialPipeline.m_defaultPropertyValues))
- {
- return RHI::ResultCode::Fail;
- }
- pipelineData.m_materialProperties.SetAllPropertyDirtyFlags();
- }
- // Register for update events related to Shader instances that own the ShaderAssets inside
- // the shader collection.
- ForAllShaderItems([this](const Name&, const ShaderCollection::Item& shaderItem)
- {
- ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get());
- ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
- return true;
- });
- // Usually SetProperties called above will increment this change ID to invalidate
- // the material, but some materials might not have any properties, and we need
- // the material to be invalidated particularly when hot-reloading.
- ++m_currentChangeId;
- Compile();
- return RHI::ResultCode::Success;
- }
- Material::~Material()
- {
- ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
- }
- const ShaderCollection& Material::GetGeneralShaderCollection() const
- {
- return m_generalShaderCollection;
- }
- const ShaderCollection& Material::GetShaderCollection(const Name& forPipeline) const
- {
- auto iter = m_materialPipelineData.find(forPipeline);
- if (iter == m_materialPipelineData.end())
- {
- static ShaderCollection EmptyShaderCollection;
- return EmptyShaderCollection;
- }
- return iter->second.m_shaderCollection;
- }
-
- void Material::ForAllShaderItemsWriteable(AZStd::function<bool(ShaderCollection::Item& shaderItem)> callback)
- {
- for (auto& shaderItem : m_generalShaderCollection)
- {
- if (!callback(shaderItem))
- {
- return;
- }
- }
- for (auto& materialPipelinePair : m_materialPipelineData)
- {
- for (auto& shaderItem : materialPipelinePair.second.m_shaderCollection)
- {
- if (!callback(shaderItem))
- {
- return;
- }
- }
- }
- }
- void Material::ForAllShaderItems(AZStd::function<bool(const Name& materialPipelineName, const ShaderCollection::Item& shaderItem)> callback) const
- {
- for (const auto& shaderItem : m_generalShaderCollection)
- {
- if (!callback(MaterialPipelineNone, shaderItem))
- {
- return;
- }
- }
- for (const auto& [materialPipelineName, materialPipeline] : m_materialPipelineData)
- {
- for (const auto& shaderItem : materialPipeline.m_shaderCollection)
- {
- if (!callback(materialPipelineName, shaderItem))
- {
- return;
- }
- }
- }
- }
- bool Material::MaterialOwnsShaderOption(const Name& shaderOptionName) const
- {
- bool isOwned = false;
- // We won't set any shader options if the shader option is owned by any of the other shaders in this material.
- // If the material uses an option in any shader, then it owns that option for all its shaders.
- ForAllShaderItems([&](const Name&, const ShaderCollection::Item& shaderItem)
- {
- const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
- ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
- if (index.IsValid())
- {
- if (shaderItem.MaterialOwnsShaderOption(index))
- {
- isOwned = true;
- return false; // We can stop searching now
- }
- }
- return true; // Continue
- });
- return isOwned;
- }
- AZ::Outcome<uint32_t> Material::SetSystemShaderOption(const Name& shaderOptionName, RPI::ShaderOptionValue value)
- {
- uint32_t appliedCount = 0;
- if (MaterialOwnsShaderOption(shaderOptionName))
- {
- return AZ::Failure();
- }
- ForAllShaderItemsWriteable([&](ShaderCollection::Item& shaderItem)
- {
- const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
- ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
- if (index.IsValid())
- {
- shaderItem.GetShaderOptions()->SetValue(index, value);
- appliedCount++;
- }
- return true;
- });
- return AZ::Success(appliedCount);
- }
- void Material::ApplyGlobalShaderOptions()
- {
- // [GFX TODO][ATOM-5625] This really needs to be optimized to put the burden on setting global shader options, not applying global shader options.
- // For example, make the shader system collect a map of all shaders and ShaderVaraintIds, and look up the shader option names at set-time.
- ShaderSystemInterface* shaderSystem = ShaderSystemInterface::Get();
- for (auto iter : shaderSystem->GetGlobalShaderOptions())
- {
- const Name& shaderOptionName = iter.first;
- ShaderOptionValue value = iter.second;
- if (!SetSystemShaderOption(shaderOptionName, value).IsSuccess())
- {
- AZ_Warning("Material", false, "Shader option '%s' is owned by this material. Global value for this option was ignored.", shaderOptionName.GetCStr());
- }
- }
- }
- void Material::SetPsoHandlingOverride(MaterialPropertyPsoHandling psoHandlingOverride)
- {
- // On some platforms, PipelineStateObjects must be pre-compiled and shipped with the game; they cannot be compiled at runtime. So at some
- // point the material system needs to be smart about when it allows PSO changes and when it doesn't. There is a task scheduled to
- // thoroughly address this in 2022, but for now we just report a warning to alert users who are using the engine in a way that might
- // not be supported for much longer. PSO changes should only be allowed in developer tools (though we could also expose a way for users to
- // enable dynamic PSO changes if their project only targets platforms that support this).
- // PSO modifications are allowed during initialization because that's using the stored asset data, which the asset system can
- // access to pre-compile the necessary PSOs.
- m_psoHandling = psoHandlingOverride;
- }
- const RHI::ShaderResourceGroup* Material::GetRHIShaderResourceGroup() const
- {
- return m_rhiShaderResourceGroup;
- }
- const Data::Asset<MaterialAsset>& Material::GetAsset() const
- {
- return m_materialAsset;
- }
- bool Material::CanCompile() const
- {
- return m_materialAsset.IsReady() && (!m_shaderResourceGroup || !m_shaderResourceGroup->IsQueuedForCompile());
- }
- ///////////////////////////////////////////////////////////////////
- // ShaderReloadNotificationBus overrides...
- void Material::OnShaderReinitialized([[maybe_unused]] const Shader& shader)
- {
- ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderReinitialized %s", this, shader.GetAsset().GetHint().c_str());
- // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with
- // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be
- // updated to correctly handle the reload, so it's safer to just reinitialize the whole material.
- ReInitKeepPropertyValues();
- }
- void Material::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>& shaderAsset)
- {
- ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str());
- // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with
- // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be
- // updated to correctly handle the reload, so it's safer to just reinitialize the whole material.
- ReInitKeepPropertyValues();
- }
- void Material::OnShaderVariantReinitialized(const ShaderVariant& shaderVariant)
- {
- ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderVariantReinitialized %s", this, shaderVariant.GetShaderVariantAsset().GetHint().c_str());
- // Note: we don't need to re-compile the material if a shader variant is ready or changed
- // The DrawPacket created for the material need to be updated since the PSO need to be re-creaed.
- // This event can be used to notify the owners to update their DrawPackets
- m_shaderVariantReadyEvent.Signal();
- }
- void Material::ReInitKeepPropertyValues()
- {
- // Save the material property values to be reapplied after reinitialization. The mapping is stored by name in case the property
- // layout changes after reinitialization.
- AZStd::unordered_map<AZ::Name, MaterialPropertyValue> properties;
- properties.reserve(GetMaterialPropertiesLayout()->GetPropertyCount());
- for (size_t propertyIndex = 0; propertyIndex < GetMaterialPropertiesLayout()->GetPropertyCount(); ++propertyIndex)
- {
- auto descriptor = GetMaterialPropertiesLayout()->GetPropertyDescriptor(AZ::RPI::MaterialPropertyIndex{ propertyIndex });
- properties.emplace(descriptor->GetName(), GetPropertyValue(AZ::RPI::MaterialPropertyIndex{ propertyIndex }));
- }
- if (Init(*m_materialAsset) == RHI::ResultCode::Success)
- {
- for (const auto& [propertyName, propertyValue] : properties)
- {
- if (const auto& propertyIndex = GetMaterialPropertiesLayout()->FindPropertyIndex(propertyName); propertyIndex.IsValid())
- {
- SetPropertyValue(propertyIndex, propertyValue);
- }
- }
- Compile();
- }
- }
- ///////////////////////////////////////////////////////////////////
- const MaterialPropertyCollection& Material::GetPropertyCollection() const
- {
- return m_materialProperties;
- }
- const MaterialPropertyValue& Material::GetPropertyValue(MaterialPropertyIndex index) const
- {
- return m_materialProperties.GetPropertyValue(index);
- }
- const AZStd::vector<MaterialPropertyValue>& Material::GetPropertyValues() const
- {
- return m_materialProperties.GetPropertyValues();
- }
- bool Material::NeedsCompile() const
- {
- return m_compiledChangeId != m_currentChangeId;
- }
- void Material::ConnectEvent(OnMaterialShaderVariantReadyEvent::Handler& handler)
- {
- handler.Connect(m_shaderVariantReadyEvent);
- }
- bool Material::TryApplyPropertyConnectionToShaderInput(
- const MaterialPropertyValue& value,
- const MaterialPropertyOutputId& connection,
- const MaterialPropertyDescriptor* propertyDescriptor)
- {
- if (connection.m_type == MaterialPropertyOutputType::ShaderInput)
- {
- if (propertyDescriptor->GetDataType() == MaterialPropertyDataType::Image)
- {
- const Data::Instance<Image>& image = value.GetValue<Data::Instance<Image>>();
- RHI::ShaderInputImageIndex shaderInputIndex(connection.m_itemIndex.GetIndex());
- m_shaderResourceGroup->SetImage(shaderInputIndex, image);
- }
- else
- {
- RHI::ShaderInputConstantIndex shaderInputIndex(connection.m_itemIndex.GetIndex());
- SetShaderConstant(shaderInputIndex, value);
- }
- return true;
- }
- return false;
- }
- bool Material::TryApplyPropertyConnectionToShaderOption(
- const MaterialPropertyValue& value,
- const MaterialPropertyOutputId& connection)
- {
- if (connection.m_type == MaterialPropertyOutputType::ShaderOption)
- {
- ShaderCollection::Item* shaderReference = nullptr;
- if (connection.m_materialPipelineName.IsEmpty())
- {
- shaderReference = &m_generalShaderCollection[connection.m_containerIndex.GetIndex()];
- }
- else
- {
- shaderReference = &m_materialPipelineData[connection.m_materialPipelineName].m_shaderCollection[connection.m_containerIndex.GetIndex()];
- }
- SetShaderOption(*shaderReference->GetShaderOptions(), ShaderOptionIndex{connection.m_itemIndex.GetIndex()}, value);
- return true;
- }
- return false;
- }
- bool Material::TryApplyPropertyConnectionToShaderEnable(
- const MaterialPropertyValue& value,
- const MaterialPropertyOutputId& connection)
- {
- if (connection.m_type == MaterialPropertyOutputType::ShaderEnabled)
- {
- ShaderCollection::Item* shaderReference = nullptr;
- if (!value.Is<bool>())
- {
- // We should never get here because MaterialTypeAssetCreator and MaterialPropertyCollection::ValidatePropertyAccess ensure the value is a bool.
- AZ_Assert(false, "Unsupported data type for MaterialPropertyOutputType::ShaderEnabled");
- return false;
- }
- if (connection.m_materialPipelineName.IsEmpty())
- {
- shaderReference = &m_generalShaderCollection[connection.m_containerIndex.GetIndex()];
- }
- else
- {
- shaderReference = &m_materialPipelineData[connection.m_materialPipelineName].m_shaderCollection[connection.m_containerIndex.GetIndex()];
- }
- shaderReference->SetEnabled(value.GetValue<bool>());
- return true;
- }
- return false;
- }
- bool Material::TryApplyPropertyConnectionToInternalProperty(
- const MaterialPropertyValue& value,
- const MaterialPropertyOutputId& connection)
- {
- if (connection.m_type == MaterialPropertyOutputType::InternalProperty)
- {
- m_materialPipelineData[connection.m_materialPipelineName].m_materialProperties.SetPropertyValue(MaterialPropertyIndex{connection.m_itemIndex.GetIndex()}, value);
- return true;
- }
- return false;
- }
- void Material::ProcessDirectConnections()
- {
- AZ_PROFILE_SCOPE(RPI, "Process direct connection");
- // Apply any changes to *main* material properties...
- for (size_t i = 0; i < m_materialProperties.GetMaterialPropertiesLayout()->GetPropertyCount(); ++i)
- {
- if (!m_materialProperties.GetPropertyDirtyFlags()[i])
- {
- continue;
- }
- MaterialPropertyIndex propertyIndex{i};
- const MaterialPropertyValue value = m_materialProperties.GetPropertyValue(propertyIndex);
- const MaterialPropertyDescriptor* propertyDescriptor =
- m_materialProperties.GetMaterialPropertiesLayout()->GetPropertyDescriptor(propertyIndex);
- for (const MaterialPropertyOutputId& connection : propertyDescriptor->GetOutputConnections())
- {
- [[maybe_unused]] bool applied =
- TryApplyPropertyConnectionToShaderInput(value, connection, propertyDescriptor) ||
- TryApplyPropertyConnectionToShaderOption(value, connection) ||
- TryApplyPropertyConnectionToShaderEnable(value, connection) ||
- TryApplyPropertyConnectionToInternalProperty(value, connection);
- AZ_Error(s_debugTraceName, applied, "Connections of type %s are not supported by material properties.", ToString(connection.m_type));
- }
- }
- }
- void Material::ProcessInternalDirectConnections()
- {
- AZ_PROFILE_SCOPE(RPI, "Process direct connection");
- // Apply any changes to *internal* material properties...
- for (auto& materialPipelinePair : m_materialPipelineData)
- {
- MaterialPropertyCollection& pipelineProperties = materialPipelinePair.second.m_materialProperties;
- const MaterialPropertiesLayout& pipelinePropertiesLayout = *materialPipelinePair.second.m_materialProperties.GetMaterialPropertiesLayout();
- for (size_t i = 0; i < pipelinePropertiesLayout.GetPropertyCount(); ++i)
- {
- if (!materialPipelinePair.second.m_materialProperties.GetPropertyDirtyFlags()[i])
- {
- continue;
- }
- MaterialPropertyIndex propertyIndex{i};
- const MaterialPropertyValue value = pipelineProperties.GetPropertyValue(propertyIndex);
- const MaterialPropertyDescriptor* propertyDescriptor = pipelinePropertiesLayout.GetPropertyDescriptor(propertyIndex);
- for (const MaterialPropertyOutputId& connection : propertyDescriptor->GetOutputConnections())
- {
- // Note that ShaderInput is not supported for internal properties. Internal properties are used exclusively for the
- // .materialpipeline which is not allowed to access to the MaterialSrg, only the .materialtype should know about the MaterialSrg.
- [[maybe_unused]] bool applied =
- TryApplyPropertyConnectionToShaderOption(value, connection) ||
- TryApplyPropertyConnectionToShaderEnable(value, connection);
- AZ_Error(s_debugTraceName, applied, "Connections of type %s are not supported by material pipeline properties.", ToString(connection.m_type));
- }
- }
- }
- }
- void Material::ProcessMaterialFunctors()
- {
- AZ_PROFILE_SCOPE(RPI, "Process material functors");
- MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling;
- // First run the "main" MaterialPipelineNone functors, which use the MaterialFunctorAPI::RuntimeContext
- for (const Ptr<MaterialFunctor>& functor : m_materialAsset->GetMaterialFunctors())
- {
- if (functor)
- {
- const MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies();
- // None covers case that the client code doesn't register material properties to dependencies,
- // which will later get caught in Process() when trying to access a property.
- if (materialPropertyDependencies.none() || functor->NeedsProcess(m_materialProperties.GetPropertyDirtyFlags()))
- {
- MaterialFunctorAPI::RuntimeContext processContext = MaterialFunctorAPI::RuntimeContext(
- m_materialProperties,
- &materialPropertyDependencies,
- psoHandling,
- m_shaderResourceGroup.get(),
- &m_generalShaderCollection,
- &m_materialPipelineData
- );
- functor->Process(processContext);
- }
- }
- else
- {
- // This could happen when the dll containing the functor class is missing. There will likely be more errors
- // preceding this one, from the serialization system when loading the material asset.
- AZ_Error(s_debugTraceName, false, "Material functor is null.");
- }
- }
- }
- void Material::ProcessInternalMaterialFunctors()
- {
- AZ_PROFILE_SCOPE(RPI, "Process material functors");
- MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling;
- // Then run the "pipeline" functors, which use the MaterialFunctorAPI::PipelineRuntimeContext
- for (auto& [materialPipelineName, materialPipeline] : m_materialAsset->GetMaterialPipelinePayloads())
- {
- MaterialPipelineState& materialPipelineData = m_materialPipelineData[materialPipelineName];
- for (const Ptr<MaterialFunctor>& functor : materialPipeline.m_materialFunctors)
- {
- if (functor)
- {
- const MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies();
- // None covers case that the client code doesn't register material properties to dependencies,
- // which will later get caught in Process() when trying to access a property.
- if (materialPropertyDependencies.none() || functor->NeedsProcess(materialPipelineData.m_materialProperties.GetPropertyDirtyFlags()))
- {
- MaterialFunctorAPI::PipelineRuntimeContext processContext = MaterialFunctorAPI::PipelineRuntimeContext(
- materialPipelineData.m_materialProperties,
- &materialPropertyDependencies,
- psoHandling,
- &materialPipelineData.m_shaderCollection
- );
- functor->Process(processContext);
- }
- }
- else
- {
- // This could happen when the dll containing the functor class is missing. There will likely be more errors
- // preceding this one, from the serialization system when loading the material asset.
- AZ_Error(s_debugTraceName, false, "Material functor is null.");
- }
- }
- }
- }
- bool Material::Compile()
- {
- AZ_PROFILE_FUNCTION(RPI);
- if (!NeedsCompile())
- {
- return true;
- }
- if (CanCompile())
- {
- ProcessDirectConnections();
- ProcessMaterialFunctors();
- ProcessInternalDirectConnections();
- ProcessInternalMaterialFunctors();
- m_materialProperties.ClearAllPropertyDirtyFlags();
- for (auto& materialPipelinePair : m_materialPipelineData)
- {
- materialPipelinePair.second.m_materialProperties.ClearAllPropertyDirtyFlags();
- }
- if (m_shaderResourceGroup)
- {
- m_shaderResourceGroup->Compile();
- }
- m_compiledChangeId = m_currentChangeId;
- return true;
- }
- return false;
- }
- Material::ChangeId Material::GetCurrentChangeId() const
- {
- return m_currentChangeId;
- }
- MaterialPropertyIndex Material::FindPropertyIndex(const Name& propertyId, bool* wasRenamed, Name* newName) const
- {
- if (wasRenamed)
- {
- *wasRenamed = false;
- }
- MaterialPropertyIndex index = m_materialProperties.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId);
- if (!index.IsValid())
- {
- Name renamedId = propertyId;
-
- if (m_materialAsset->GetMaterialTypeAsset()->ApplyPropertyRenames(renamedId))
- {
- index = m_materialProperties.GetMaterialPropertiesLayout()->FindPropertyIndex(renamedId);
- if (wasRenamed)
- {
- *wasRenamed = true;
- }
- if (newName)
- {
- *newName = renamedId;
- }
- AZ_Warning("Material", false,
- "Material property '%s' has been renamed to '%s'. Consider updating the corresponding source data.",
- propertyId.GetCStr(),
- renamedId.GetCStr());
- }
- }
- return index;
- }
- bool Material::SetShaderConstant(RHI::ShaderInputConstantIndex shaderInputIndex, const MaterialPropertyValue& value)
- {
- if (!value.IsValid())
- {
- AZ_Assert(false, "Empty value found for shader input index %u", shaderInputIndex.GetIndex());
- return false;
- }
- else if (value.Is<bool>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<bool>());
- }
- else if (value.Is<int32_t>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<int32_t>());
- }
- else if (value.Is<uint32_t>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<uint32_t>());
- }
- else if (value.Is<float>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<float>());
- }
- else if (value.Is<Vector2>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Vector2>());
- }
- else if (value.Is<Vector3>())
- {
- // Vector3 is actually 16 bytes, not 12, so ShaderResourceGroup::SetConstant won't work. We
- // have to pass the raw data instead.
- return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &value.GetValue<Vector3>(), 3 * sizeof(float));
- }
- else if (value.Is<Vector4>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Vector4>());
- }
- else if (value.Is<Color>())
- {
- auto transformedColor = AZ::RPI::TransformColor(value.GetValue<Color>(), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
- // Color is special because it could map to either a float3 or a float4
- auto descriptor = m_shaderResourceGroup->GetLayout()->GetShaderInput(shaderInputIndex);
- if (descriptor.m_constantByteCount == 3 * sizeof(float))
- {
- return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &transformedColor, 3 * sizeof(float));
- }
- else
- {
- return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &transformedColor, 4 * sizeof(float));
- }
- }
- else if (value.Is<Data::Instance<Image>>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Data::Instance<Image>>());
- }
- else if (value.Is<Data::Asset<ImageAsset>>())
- {
- return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Data::Asset<ImageAsset>>());
- }
- else
- {
- AZ_Assert(false, "Unhandled material property value type");
- return false;
- }
- }
- bool Material::SetShaderOption(ShaderOptionGroup& options, ShaderOptionIndex shaderOptionIndex, const MaterialPropertyValue& value)
- {
- if (!value.IsValid())
- {
- AZ_Assert(false, "Empty value found for shader option %u", shaderOptionIndex.GetIndex());
- return false;
- }
- else if (value.Is<bool>())
- {
- return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<bool>()});
- }
- else if (value.Is<int32_t>())
- {
- return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<int32_t>()});
- }
- else if (value.Is<uint32_t>())
- {
- return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<uint32_t>()});
- }
- else
- {
- AZ_Assert(false, "MaterialProperty is incorrectly mapped to a shader option. Data type is incompatible.");
- return false;
- }
- }
- template<typename Type>
- bool Material::SetPropertyValue(MaterialPropertyIndex index, const Type& value)
- {
- bool success = m_materialProperties.SetPropertyValue<Type>(index, value);
- if (success)
- {
- ++m_currentChangeId;
- }
- return success;
- }
- // Using explicit instantiation to restrict SetPropertyValue to the set of types that we support
- template bool Material::SetPropertyValue<bool> (MaterialPropertyIndex index, const bool& value);
- template bool Material::SetPropertyValue<int32_t> (MaterialPropertyIndex index, const int32_t& value);
- template bool Material::SetPropertyValue<uint32_t> (MaterialPropertyIndex index, const uint32_t& value);
- template bool Material::SetPropertyValue<float> (MaterialPropertyIndex index, const float& value);
- template bool Material::SetPropertyValue<Vector2> (MaterialPropertyIndex index, const Vector2& value);
- template bool Material::SetPropertyValue<Vector3> (MaterialPropertyIndex index, const Vector3& value);
- template bool Material::SetPropertyValue<Vector4> (MaterialPropertyIndex index, const Vector4& value);
- template bool Material::SetPropertyValue<Color> (MaterialPropertyIndex index, const Color& value);
- template bool Material::SetPropertyValue<Data::Instance<Image>> (MaterialPropertyIndex index, const Data::Instance<Image>& value);
- bool Material::SetPropertyValue(MaterialPropertyIndex propertyIndex, const MaterialPropertyValue& value)
- {
- bool success = m_materialProperties.SetPropertyValue(propertyIndex, value);
- if (success)
- {
- ++m_currentChangeId;
- }
- return success;
- }
- template<typename Type>
- const Type& Material::GetPropertyValue(MaterialPropertyIndex index) const
- {
- return m_materialProperties.GetPropertyValue<Type>(index);
- }
- // Using explicit instantiation to restrict GetPropertyValue to the set of types that we support
- template const bool& Material::GetPropertyValue<bool> (MaterialPropertyIndex index) const;
- template const int32_t& Material::GetPropertyValue<int32_t> (MaterialPropertyIndex index) const;
- template const uint32_t& Material::GetPropertyValue<uint32_t> (MaterialPropertyIndex index) const;
- template const float& Material::GetPropertyValue<float> (MaterialPropertyIndex index) const;
- template const Vector2& Material::GetPropertyValue<Vector2> (MaterialPropertyIndex index) const;
- template const Vector3& Material::GetPropertyValue<Vector3> (MaterialPropertyIndex index) const;
- template const Vector4& Material::GetPropertyValue<Vector4> (MaterialPropertyIndex index) const;
- template const Color& Material::GetPropertyValue<Color> (MaterialPropertyIndex index) const;
- template const Data::Instance<Image>& Material::GetPropertyValue<Data::Instance<Image>>(MaterialPropertyIndex index) const;
-
- const MaterialPropertyFlags& Material::GetPropertyDirtyFlags() const
- {
- return m_materialProperties.GetPropertyDirtyFlags();
- }
- RHI::ConstPtr<MaterialPropertiesLayout> Material::GetMaterialPropertiesLayout() const
- {
- return m_materialProperties.GetMaterialPropertiesLayout();
- }
- Data::Instance<RPI::ShaderResourceGroup> Material::GetShaderResourceGroup()
- {
- return m_shaderResourceGroup;
- }
- } // namespace RPI
- } // namespace AZ
|