3
0

Material.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/RPI.Public/ColorManagement/TransformColor.h>
  9. #include <Atom/RPI.Public/Material/Material.h>
  10. #include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
  11. #include <Atom/RPI.Public/Image/AttachmentImage.h>
  12. #include <Atom/RPI.Public/Image/StreamingImage.h>
  13. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  14. #include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
  15. #include <Atom/RPI.Public/Shader/Shader.h>
  16. #include <Atom/RPI.Public/Shader/ShaderSystemInterface.h>
  17. #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
  18. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  19. #include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
  20. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  21. #include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
  22. #include <AtomCore/Instance/InstanceDatabase.h>
  23. #include <AtomCore/Utils/ScopedValue.h>
  24. namespace AZ
  25. {
  26. namespace RPI
  27. {
  28. const char* Material::s_debugTraceName = "Material";
  29. Data::Instance<Material> Material::FindOrCreate(const Data::Asset<MaterialAsset>& materialAsset)
  30. {
  31. return Data::InstanceDatabase<Material>::Instance().FindOrCreate(materialAsset);
  32. }
  33. Data::Instance<Material> Material::Create(const Data::Asset<MaterialAsset>& materialAsset)
  34. {
  35. return Data::InstanceDatabase<Material>::Instance().Create(materialAsset);
  36. }
  37. AZ::Data::Instance<Material> Material::CreateInternal(MaterialAsset& materialAsset)
  38. {
  39. Data::Instance<Material> material = aznew Material();
  40. const RHI::ResultCode resultCode = material->Init(materialAsset);
  41. if (resultCode == RHI::ResultCode::Success)
  42. {
  43. return material;
  44. }
  45. return nullptr;
  46. }
  47. RHI::ResultCode Material::Init(MaterialAsset& materialAsset)
  48. {
  49. AZ_PROFILE_FUNCTION(RPI);
  50. ScopedValue isInitializing(&m_isInitializing, true, false);
  51. // All of these members must be reset if the material can be reinitialized because of the shader reload notification bus
  52. m_shaderResourceGroup = {};
  53. m_rhiShaderResourceGroup = {};
  54. m_materialProperties = {};
  55. m_generalShaderCollection = {};
  56. m_materialPipelineData = {};
  57. m_materialAsset = { &materialAsset, AZ::Data::AssetLoadBehavior::PreLoad };
  58. ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
  59. if (!m_materialAsset.IsReady())
  60. {
  61. AZ_Error(s_debugTraceName, false, "Material::Init failed because shader asset is not ready. materialAsset uuid=%s",
  62. materialAsset.GetId().ToFixedString().c_str());
  63. return RHI::ResultCode::Fail;
  64. }
  65. if (!m_materialAsset->InitializeNonSerializedData())
  66. {
  67. AZ_Error(s_debugTraceName, false, "Material::InitializeNonSerializedData is not supposed to fail. materialAsset uuid=%s",
  68. materialAsset.GetId().ToFixedString().c_str());
  69. return RHI::ResultCode::Fail;
  70. }
  71. // Cache off pointers to some key data structures from the material type...
  72. auto srgLayout = m_materialAsset->GetMaterialSrgLayout();
  73. if (srgLayout)
  74. {
  75. auto shaderAsset = m_materialAsset->GetMaterialTypeAsset()->GetShaderAssetForMaterialSrg();
  76. m_shaderResourceGroup = ShaderResourceGroup::Create(shaderAsset, srgLayout->GetName());
  77. if (m_shaderResourceGroup)
  78. {
  79. m_rhiShaderResourceGroup = m_shaderResourceGroup->GetRHIShaderResourceGroup();
  80. }
  81. else
  82. {
  83. // No need to report an error message here, ShaderResourceGroup::Create() will have reported.
  84. return RHI::ResultCode::Fail;
  85. }
  86. }
  87. m_generalShaderCollection = m_materialAsset->GetGeneralShaderCollection();
  88. if (!m_materialProperties.Init(m_materialAsset->GetMaterialPropertiesLayout(), m_materialAsset->GetPropertyValues()))
  89. {
  90. return RHI::ResultCode::Fail;
  91. }
  92. m_materialProperties.SetAllPropertyDirtyFlags();
  93. for (auto& [materialPipelineName, materialPipeline] : m_materialAsset->GetMaterialPipelinePayloads())
  94. {
  95. MaterialPipelineState& pipelineData = m_materialPipelineData[materialPipelineName];
  96. pipelineData.m_shaderCollection = materialPipeline.m_shaderCollection;
  97. if (!pipelineData.m_materialProperties.Init(materialPipeline.m_materialPropertiesLayout, materialPipeline.m_defaultPropertyValues))
  98. {
  99. return RHI::ResultCode::Fail;
  100. }
  101. pipelineData.m_materialProperties.SetAllPropertyDirtyFlags();
  102. }
  103. // Register for update events related to Shader instances that own the ShaderAssets inside
  104. // the shader collection.
  105. ForAllShaderItems([this](const Name&, const ShaderCollection::Item& shaderItem)
  106. {
  107. ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get());
  108. ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
  109. return true;
  110. });
  111. // Usually SetProperties called above will increment this change ID to invalidate
  112. // the material, but some materials might not have any properties, and we need
  113. // the material to be invalidated particularly when hot-reloading.
  114. ++m_currentChangeId;
  115. Compile();
  116. return RHI::ResultCode::Success;
  117. }
  118. Material::~Material()
  119. {
  120. ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
  121. }
  122. const ShaderCollection& Material::GetGeneralShaderCollection() const
  123. {
  124. return m_generalShaderCollection;
  125. }
  126. const ShaderCollection& Material::GetShaderCollection(const Name& forPipeline) const
  127. {
  128. auto iter = m_materialPipelineData.find(forPipeline);
  129. if (iter == m_materialPipelineData.end())
  130. {
  131. static ShaderCollection EmptyShaderCollection;
  132. return EmptyShaderCollection;
  133. }
  134. return iter->second.m_shaderCollection;
  135. }
  136. void Material::ForAllShaderItemsWriteable(AZStd::function<bool(ShaderCollection::Item& shaderItem)> callback)
  137. {
  138. for (auto& shaderItem : m_generalShaderCollection)
  139. {
  140. if (!callback(shaderItem))
  141. {
  142. return;
  143. }
  144. }
  145. for (auto& materialPipelinePair : m_materialPipelineData)
  146. {
  147. for (auto& shaderItem : materialPipelinePair.second.m_shaderCollection)
  148. {
  149. if (!callback(shaderItem))
  150. {
  151. return;
  152. }
  153. }
  154. }
  155. }
  156. void Material::ForAllShaderItems(AZStd::function<bool(const Name& materialPipelineName, const ShaderCollection::Item& shaderItem)> callback) const
  157. {
  158. for (const auto& shaderItem : m_generalShaderCollection)
  159. {
  160. if (!callback(MaterialPipelineNone, shaderItem))
  161. {
  162. return;
  163. }
  164. }
  165. for (const auto& [materialPipelineName, materialPipeline] : m_materialPipelineData)
  166. {
  167. for (const auto& shaderItem : materialPipeline.m_shaderCollection)
  168. {
  169. if (!callback(materialPipelineName, shaderItem))
  170. {
  171. return;
  172. }
  173. }
  174. }
  175. }
  176. bool Material::MaterialOwnsShaderOption(const Name& shaderOptionName) const
  177. {
  178. bool isOwned = false;
  179. // We won't set any shader options if the shader option is owned by any of the other shaders in this material.
  180. // If the material uses an option in any shader, then it owns that option for all its shaders.
  181. ForAllShaderItems([&](const Name&, const ShaderCollection::Item& shaderItem)
  182. {
  183. const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
  184. ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
  185. if (index.IsValid())
  186. {
  187. if (shaderItem.MaterialOwnsShaderOption(index))
  188. {
  189. isOwned = true;
  190. return false; // We can stop searching now
  191. }
  192. }
  193. return true; // Continue
  194. });
  195. return isOwned;
  196. }
  197. AZ::Outcome<uint32_t> Material::SetSystemShaderOption(const Name& shaderOptionName, RPI::ShaderOptionValue value)
  198. {
  199. uint32_t appliedCount = 0;
  200. if (MaterialOwnsShaderOption(shaderOptionName))
  201. {
  202. return AZ::Failure();
  203. }
  204. ForAllShaderItemsWriteable([&](ShaderCollection::Item& shaderItem)
  205. {
  206. const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
  207. ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
  208. if (index.IsValid())
  209. {
  210. shaderItem.GetShaderOptions()->SetValue(index, value);
  211. appliedCount++;
  212. }
  213. return true;
  214. });
  215. return AZ::Success(appliedCount);
  216. }
  217. void Material::ApplyGlobalShaderOptions()
  218. {
  219. // [GFX TODO][ATOM-5625] This really needs to be optimized to put the burden on setting global shader options, not applying global shader options.
  220. // For example, make the shader system collect a map of all shaders and ShaderVaraintIds, and look up the shader option names at set-time.
  221. ShaderSystemInterface* shaderSystem = ShaderSystemInterface::Get();
  222. for (auto iter : shaderSystem->GetGlobalShaderOptions())
  223. {
  224. const Name& shaderOptionName = iter.first;
  225. ShaderOptionValue value = iter.second;
  226. if (!SetSystemShaderOption(shaderOptionName, value).IsSuccess())
  227. {
  228. AZ_Warning("Material", false, "Shader option '%s' is owned by this material. Global value for this option was ignored.", shaderOptionName.GetCStr());
  229. }
  230. }
  231. }
  232. void Material::SetPsoHandlingOverride(MaterialPropertyPsoHandling psoHandlingOverride)
  233. {
  234. // On some platforms, PipelineStateObjects must be pre-compiled and shipped with the game; they cannot be compiled at runtime. So at some
  235. // 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
  236. // 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
  237. // 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
  238. // enable dynamic PSO changes if their project only targets platforms that support this).
  239. // PSO modifications are allowed during initialization because that's using the stored asset data, which the asset system can
  240. // access to pre-compile the necessary PSOs.
  241. m_psoHandling = psoHandlingOverride;
  242. }
  243. const RHI::ShaderResourceGroup* Material::GetRHIShaderResourceGroup() const
  244. {
  245. return m_rhiShaderResourceGroup;
  246. }
  247. const Data::Asset<MaterialAsset>& Material::GetAsset() const
  248. {
  249. return m_materialAsset;
  250. }
  251. bool Material::CanCompile() const
  252. {
  253. return m_materialAsset.IsReady() && (!m_shaderResourceGroup || !m_shaderResourceGroup->IsQueuedForCompile());
  254. }
  255. ///////////////////////////////////////////////////////////////////
  256. // ShaderReloadNotificationBus overrides...
  257. void Material::OnShaderReinitialized([[maybe_unused]] const Shader& shader)
  258. {
  259. ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderReinitialized %s", this, shader.GetAsset().GetHint().c_str());
  260. // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with
  261. // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be
  262. // updated to correctly handle the reload, so it's safer to just reinitialize the whole material.
  263. ReInitKeepPropertyValues();
  264. }
  265. void Material::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>& shaderAsset)
  266. {
  267. ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str());
  268. // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with
  269. // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be
  270. // updated to correctly handle the reload, so it's safer to just reinitialize the whole material.
  271. ReInitKeepPropertyValues();
  272. }
  273. void Material::OnShaderVariantReinitialized(const ShaderVariant& shaderVariant)
  274. {
  275. ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderVariantReinitialized %s", this, shaderVariant.GetShaderVariantAsset().GetHint().c_str());
  276. // Note: we don't need to re-compile the material if a shader variant is ready or changed
  277. // The DrawPacket created for the material need to be updated since the PSO need to be re-creaed.
  278. // This event can be used to notify the owners to update their DrawPackets
  279. m_shaderVariantReadyEvent.Signal();
  280. }
  281. void Material::ReInitKeepPropertyValues()
  282. {
  283. // Save the material property values to be reapplied after reinitialization. The mapping is stored by name in case the property
  284. // layout changes after reinitialization.
  285. AZStd::unordered_map<AZ::Name, MaterialPropertyValue> properties;
  286. properties.reserve(GetMaterialPropertiesLayout()->GetPropertyCount());
  287. for (size_t propertyIndex = 0; propertyIndex < GetMaterialPropertiesLayout()->GetPropertyCount(); ++propertyIndex)
  288. {
  289. auto descriptor = GetMaterialPropertiesLayout()->GetPropertyDescriptor(AZ::RPI::MaterialPropertyIndex{ propertyIndex });
  290. properties.emplace(descriptor->GetName(), GetPropertyValue(AZ::RPI::MaterialPropertyIndex{ propertyIndex }));
  291. }
  292. if (Init(*m_materialAsset) == RHI::ResultCode::Success)
  293. {
  294. for (const auto& [propertyName, propertyValue] : properties)
  295. {
  296. if (const auto& propertyIndex = GetMaterialPropertiesLayout()->FindPropertyIndex(propertyName); propertyIndex.IsValid())
  297. {
  298. SetPropertyValue(propertyIndex, propertyValue);
  299. }
  300. }
  301. Compile();
  302. }
  303. }
  304. ///////////////////////////////////////////////////////////////////
  305. const MaterialPropertyCollection& Material::GetPropertyCollection() const
  306. {
  307. return m_materialProperties;
  308. }
  309. const MaterialPropertyValue& Material::GetPropertyValue(MaterialPropertyIndex index) const
  310. {
  311. return m_materialProperties.GetPropertyValue(index);
  312. }
  313. const AZStd::vector<MaterialPropertyValue>& Material::GetPropertyValues() const
  314. {
  315. return m_materialProperties.GetPropertyValues();
  316. }
  317. bool Material::NeedsCompile() const
  318. {
  319. return m_compiledChangeId != m_currentChangeId;
  320. }
  321. void Material::ConnectEvent(OnMaterialShaderVariantReadyEvent::Handler& handler)
  322. {
  323. handler.Connect(m_shaderVariantReadyEvent);
  324. }
  325. bool Material::TryApplyPropertyConnectionToShaderInput(
  326. const MaterialPropertyValue& value,
  327. const MaterialPropertyOutputId& connection,
  328. const MaterialPropertyDescriptor* propertyDescriptor)
  329. {
  330. if (connection.m_type == MaterialPropertyOutputType::ShaderInput)
  331. {
  332. if (propertyDescriptor->GetDataType() == MaterialPropertyDataType::Image)
  333. {
  334. const Data::Instance<Image>& image = value.GetValue<Data::Instance<Image>>();
  335. RHI::ShaderInputImageIndex shaderInputIndex(connection.m_itemIndex.GetIndex());
  336. m_shaderResourceGroup->SetImage(shaderInputIndex, image);
  337. }
  338. else
  339. {
  340. RHI::ShaderInputConstantIndex shaderInputIndex(connection.m_itemIndex.GetIndex());
  341. SetShaderConstant(shaderInputIndex, value);
  342. }
  343. return true;
  344. }
  345. return false;
  346. }
  347. bool Material::TryApplyPropertyConnectionToShaderOption(
  348. const MaterialPropertyValue& value,
  349. const MaterialPropertyOutputId& connection)
  350. {
  351. if (connection.m_type == MaterialPropertyOutputType::ShaderOption)
  352. {
  353. ShaderCollection::Item* shaderReference = nullptr;
  354. if (connection.m_materialPipelineName.IsEmpty())
  355. {
  356. shaderReference = &m_generalShaderCollection[connection.m_containerIndex.GetIndex()];
  357. }
  358. else
  359. {
  360. shaderReference = &m_materialPipelineData[connection.m_materialPipelineName].m_shaderCollection[connection.m_containerIndex.GetIndex()];
  361. }
  362. SetShaderOption(*shaderReference->GetShaderOptions(), ShaderOptionIndex{connection.m_itemIndex.GetIndex()}, value);
  363. return true;
  364. }
  365. return false;
  366. }
  367. bool Material::TryApplyPropertyConnectionToShaderEnable(
  368. const MaterialPropertyValue& value,
  369. const MaterialPropertyOutputId& connection)
  370. {
  371. if (connection.m_type == MaterialPropertyOutputType::ShaderEnabled)
  372. {
  373. ShaderCollection::Item* shaderReference = nullptr;
  374. if (!value.Is<bool>())
  375. {
  376. // We should never get here because MaterialTypeAssetCreator and MaterialPropertyCollection::ValidatePropertyAccess ensure the value is a bool.
  377. AZ_Assert(false, "Unsupported data type for MaterialPropertyOutputType::ShaderEnabled");
  378. return false;
  379. }
  380. if (connection.m_materialPipelineName.IsEmpty())
  381. {
  382. shaderReference = &m_generalShaderCollection[connection.m_containerIndex.GetIndex()];
  383. }
  384. else
  385. {
  386. shaderReference = &m_materialPipelineData[connection.m_materialPipelineName].m_shaderCollection[connection.m_containerIndex.GetIndex()];
  387. }
  388. shaderReference->SetEnabled(value.GetValue<bool>());
  389. return true;
  390. }
  391. return false;
  392. }
  393. bool Material::TryApplyPropertyConnectionToInternalProperty(
  394. const MaterialPropertyValue& value,
  395. const MaterialPropertyOutputId& connection)
  396. {
  397. if (connection.m_type == MaterialPropertyOutputType::InternalProperty)
  398. {
  399. m_materialPipelineData[connection.m_materialPipelineName].m_materialProperties.SetPropertyValue(MaterialPropertyIndex{connection.m_itemIndex.GetIndex()}, value);
  400. return true;
  401. }
  402. return false;
  403. }
  404. void Material::ProcessDirectConnections()
  405. {
  406. AZ_PROFILE_SCOPE(RPI, "Process direct connection");
  407. // Apply any changes to *main* material properties...
  408. for (size_t i = 0; i < m_materialProperties.GetMaterialPropertiesLayout()->GetPropertyCount(); ++i)
  409. {
  410. if (!m_materialProperties.GetPropertyDirtyFlags()[i])
  411. {
  412. continue;
  413. }
  414. MaterialPropertyIndex propertyIndex{i};
  415. const MaterialPropertyValue value = m_materialProperties.GetPropertyValue(propertyIndex);
  416. const MaterialPropertyDescriptor* propertyDescriptor =
  417. m_materialProperties.GetMaterialPropertiesLayout()->GetPropertyDescriptor(propertyIndex);
  418. for (const MaterialPropertyOutputId& connection : propertyDescriptor->GetOutputConnections())
  419. {
  420. [[maybe_unused]] bool applied =
  421. TryApplyPropertyConnectionToShaderInput(value, connection, propertyDescriptor) ||
  422. TryApplyPropertyConnectionToShaderOption(value, connection) ||
  423. TryApplyPropertyConnectionToShaderEnable(value, connection) ||
  424. TryApplyPropertyConnectionToInternalProperty(value, connection);
  425. AZ_Error(s_debugTraceName, applied, "Connections of type %s are not supported by material properties.", ToString(connection.m_type));
  426. }
  427. }
  428. }
  429. void Material::ProcessInternalDirectConnections()
  430. {
  431. AZ_PROFILE_SCOPE(RPI, "Process direct connection");
  432. // Apply any changes to *internal* material properties...
  433. for (auto& materialPipelinePair : m_materialPipelineData)
  434. {
  435. MaterialPropertyCollection& pipelineProperties = materialPipelinePair.second.m_materialProperties;
  436. const MaterialPropertiesLayout& pipelinePropertiesLayout = *materialPipelinePair.second.m_materialProperties.GetMaterialPropertiesLayout();
  437. for (size_t i = 0; i < pipelinePropertiesLayout.GetPropertyCount(); ++i)
  438. {
  439. if (!materialPipelinePair.second.m_materialProperties.GetPropertyDirtyFlags()[i])
  440. {
  441. continue;
  442. }
  443. MaterialPropertyIndex propertyIndex{i};
  444. const MaterialPropertyValue value = pipelineProperties.GetPropertyValue(propertyIndex);
  445. const MaterialPropertyDescriptor* propertyDescriptor = pipelinePropertiesLayout.GetPropertyDescriptor(propertyIndex);
  446. for (const MaterialPropertyOutputId& connection : propertyDescriptor->GetOutputConnections())
  447. {
  448. // Note that ShaderInput is not supported for internal properties. Internal properties are used exclusively for the
  449. // .materialpipeline which is not allowed to access to the MaterialSrg, only the .materialtype should know about the MaterialSrg.
  450. [[maybe_unused]] bool applied =
  451. TryApplyPropertyConnectionToShaderOption(value, connection) ||
  452. TryApplyPropertyConnectionToShaderEnable(value, connection);
  453. AZ_Error(s_debugTraceName, applied, "Connections of type %s are not supported by material pipeline properties.", ToString(connection.m_type));
  454. }
  455. }
  456. }
  457. }
  458. void Material::ProcessMaterialFunctors()
  459. {
  460. AZ_PROFILE_SCOPE(RPI, "Process material functors");
  461. MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling;
  462. // First run the "main" MaterialPipelineNone functors, which use the MaterialFunctorAPI::RuntimeContext
  463. for (const Ptr<MaterialFunctor>& functor : m_materialAsset->GetMaterialFunctors())
  464. {
  465. if (functor)
  466. {
  467. const MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies();
  468. // None covers case that the client code doesn't register material properties to dependencies,
  469. // which will later get caught in Process() when trying to access a property.
  470. if (materialPropertyDependencies.none() || functor->NeedsProcess(m_materialProperties.GetPropertyDirtyFlags()))
  471. {
  472. MaterialFunctorAPI::RuntimeContext processContext = MaterialFunctorAPI::RuntimeContext(
  473. m_materialProperties,
  474. &materialPropertyDependencies,
  475. psoHandling,
  476. m_shaderResourceGroup.get(),
  477. &m_generalShaderCollection,
  478. &m_materialPipelineData
  479. );
  480. functor->Process(processContext);
  481. }
  482. }
  483. else
  484. {
  485. // This could happen when the dll containing the functor class is missing. There will likely be more errors
  486. // preceding this one, from the serialization system when loading the material asset.
  487. AZ_Error(s_debugTraceName, false, "Material functor is null.");
  488. }
  489. }
  490. }
  491. void Material::ProcessInternalMaterialFunctors()
  492. {
  493. AZ_PROFILE_SCOPE(RPI, "Process material functors");
  494. MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling;
  495. // Then run the "pipeline" functors, which use the MaterialFunctorAPI::PipelineRuntimeContext
  496. for (auto& [materialPipelineName, materialPipeline] : m_materialAsset->GetMaterialPipelinePayloads())
  497. {
  498. MaterialPipelineState& materialPipelineData = m_materialPipelineData[materialPipelineName];
  499. for (const Ptr<MaterialFunctor>& functor : materialPipeline.m_materialFunctors)
  500. {
  501. if (functor)
  502. {
  503. const MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies();
  504. // None covers case that the client code doesn't register material properties to dependencies,
  505. // which will later get caught in Process() when trying to access a property.
  506. if (materialPropertyDependencies.none() || functor->NeedsProcess(materialPipelineData.m_materialProperties.GetPropertyDirtyFlags()))
  507. {
  508. MaterialFunctorAPI::PipelineRuntimeContext processContext = MaterialFunctorAPI::PipelineRuntimeContext(
  509. materialPipelineData.m_materialProperties,
  510. &materialPropertyDependencies,
  511. psoHandling,
  512. &materialPipelineData.m_shaderCollection
  513. );
  514. functor->Process(processContext);
  515. }
  516. }
  517. else
  518. {
  519. // This could happen when the dll containing the functor class is missing. There will likely be more errors
  520. // preceding this one, from the serialization system when loading the material asset.
  521. AZ_Error(s_debugTraceName, false, "Material functor is null.");
  522. }
  523. }
  524. }
  525. }
  526. bool Material::Compile()
  527. {
  528. AZ_PROFILE_FUNCTION(RPI);
  529. if (!NeedsCompile())
  530. {
  531. return true;
  532. }
  533. if (CanCompile())
  534. {
  535. ProcessDirectConnections();
  536. ProcessMaterialFunctors();
  537. ProcessInternalDirectConnections();
  538. ProcessInternalMaterialFunctors();
  539. m_materialProperties.ClearAllPropertyDirtyFlags();
  540. for (auto& materialPipelinePair : m_materialPipelineData)
  541. {
  542. materialPipelinePair.second.m_materialProperties.ClearAllPropertyDirtyFlags();
  543. }
  544. if (m_shaderResourceGroup)
  545. {
  546. m_shaderResourceGroup->Compile();
  547. }
  548. m_compiledChangeId = m_currentChangeId;
  549. return true;
  550. }
  551. return false;
  552. }
  553. Material::ChangeId Material::GetCurrentChangeId() const
  554. {
  555. return m_currentChangeId;
  556. }
  557. MaterialPropertyIndex Material::FindPropertyIndex(const Name& propertyId, bool* wasRenamed, Name* newName) const
  558. {
  559. if (wasRenamed)
  560. {
  561. *wasRenamed = false;
  562. }
  563. MaterialPropertyIndex index = m_materialProperties.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId);
  564. if (!index.IsValid())
  565. {
  566. Name renamedId = propertyId;
  567. if (m_materialAsset->GetMaterialTypeAsset()->ApplyPropertyRenames(renamedId))
  568. {
  569. index = m_materialProperties.GetMaterialPropertiesLayout()->FindPropertyIndex(renamedId);
  570. if (wasRenamed)
  571. {
  572. *wasRenamed = true;
  573. }
  574. if (newName)
  575. {
  576. *newName = renamedId;
  577. }
  578. AZ_Warning("Material", false,
  579. "Material property '%s' has been renamed to '%s'. Consider updating the corresponding source data.",
  580. propertyId.GetCStr(),
  581. renamedId.GetCStr());
  582. }
  583. }
  584. return index;
  585. }
  586. bool Material::SetShaderConstant(RHI::ShaderInputConstantIndex shaderInputIndex, const MaterialPropertyValue& value)
  587. {
  588. if (!value.IsValid())
  589. {
  590. AZ_Assert(false, "Empty value found for shader input index %u", shaderInputIndex.GetIndex());
  591. return false;
  592. }
  593. else if (value.Is<bool>())
  594. {
  595. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<bool>());
  596. }
  597. else if (value.Is<int32_t>())
  598. {
  599. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<int32_t>());
  600. }
  601. else if (value.Is<uint32_t>())
  602. {
  603. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<uint32_t>());
  604. }
  605. else if (value.Is<float>())
  606. {
  607. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<float>());
  608. }
  609. else if (value.Is<Vector2>())
  610. {
  611. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Vector2>());
  612. }
  613. else if (value.Is<Vector3>())
  614. {
  615. // Vector3 is actually 16 bytes, not 12, so ShaderResourceGroup::SetConstant won't work. We
  616. // have to pass the raw data instead.
  617. return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &value.GetValue<Vector3>(), 3 * sizeof(float));
  618. }
  619. else if (value.Is<Vector4>())
  620. {
  621. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Vector4>());
  622. }
  623. else if (value.Is<Color>())
  624. {
  625. auto transformedColor = AZ::RPI::TransformColor(value.GetValue<Color>(), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
  626. // Color is special because it could map to either a float3 or a float4
  627. auto descriptor = m_shaderResourceGroup->GetLayout()->GetShaderInput(shaderInputIndex);
  628. if (descriptor.m_constantByteCount == 3 * sizeof(float))
  629. {
  630. return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &transformedColor, 3 * sizeof(float));
  631. }
  632. else
  633. {
  634. return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &transformedColor, 4 * sizeof(float));
  635. }
  636. }
  637. else if (value.Is<Data::Instance<Image>>())
  638. {
  639. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Data::Instance<Image>>());
  640. }
  641. else if (value.Is<Data::Asset<ImageAsset>>())
  642. {
  643. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Data::Asset<ImageAsset>>());
  644. }
  645. else
  646. {
  647. AZ_Assert(false, "Unhandled material property value type");
  648. return false;
  649. }
  650. }
  651. bool Material::SetShaderOption(ShaderOptionGroup& options, ShaderOptionIndex shaderOptionIndex, const MaterialPropertyValue& value)
  652. {
  653. if (!value.IsValid())
  654. {
  655. AZ_Assert(false, "Empty value found for shader option %u", shaderOptionIndex.GetIndex());
  656. return false;
  657. }
  658. else if (value.Is<bool>())
  659. {
  660. return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<bool>()});
  661. }
  662. else if (value.Is<int32_t>())
  663. {
  664. return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<int32_t>()});
  665. }
  666. else if (value.Is<uint32_t>())
  667. {
  668. return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<uint32_t>()});
  669. }
  670. else
  671. {
  672. AZ_Assert(false, "MaterialProperty is incorrectly mapped to a shader option. Data type is incompatible.");
  673. return false;
  674. }
  675. }
  676. template<typename Type>
  677. bool Material::SetPropertyValue(MaterialPropertyIndex index, const Type& value)
  678. {
  679. bool success = m_materialProperties.SetPropertyValue<Type>(index, value);
  680. if (success)
  681. {
  682. ++m_currentChangeId;
  683. }
  684. return success;
  685. }
  686. // Using explicit instantiation to restrict SetPropertyValue to the set of types that we support
  687. template bool Material::SetPropertyValue<bool> (MaterialPropertyIndex index, const bool& value);
  688. template bool Material::SetPropertyValue<int32_t> (MaterialPropertyIndex index, const int32_t& value);
  689. template bool Material::SetPropertyValue<uint32_t> (MaterialPropertyIndex index, const uint32_t& value);
  690. template bool Material::SetPropertyValue<float> (MaterialPropertyIndex index, const float& value);
  691. template bool Material::SetPropertyValue<Vector2> (MaterialPropertyIndex index, const Vector2& value);
  692. template bool Material::SetPropertyValue<Vector3> (MaterialPropertyIndex index, const Vector3& value);
  693. template bool Material::SetPropertyValue<Vector4> (MaterialPropertyIndex index, const Vector4& value);
  694. template bool Material::SetPropertyValue<Color> (MaterialPropertyIndex index, const Color& value);
  695. template bool Material::SetPropertyValue<Data::Instance<Image>> (MaterialPropertyIndex index, const Data::Instance<Image>& value);
  696. bool Material::SetPropertyValue(MaterialPropertyIndex propertyIndex, const MaterialPropertyValue& value)
  697. {
  698. bool success = m_materialProperties.SetPropertyValue(propertyIndex, value);
  699. if (success)
  700. {
  701. ++m_currentChangeId;
  702. }
  703. return success;
  704. }
  705. template<typename Type>
  706. const Type& Material::GetPropertyValue(MaterialPropertyIndex index) const
  707. {
  708. return m_materialProperties.GetPropertyValue<Type>(index);
  709. }
  710. // Using explicit instantiation to restrict GetPropertyValue to the set of types that we support
  711. template const bool& Material::GetPropertyValue<bool> (MaterialPropertyIndex index) const;
  712. template const int32_t& Material::GetPropertyValue<int32_t> (MaterialPropertyIndex index) const;
  713. template const uint32_t& Material::GetPropertyValue<uint32_t> (MaterialPropertyIndex index) const;
  714. template const float& Material::GetPropertyValue<float> (MaterialPropertyIndex index) const;
  715. template const Vector2& Material::GetPropertyValue<Vector2> (MaterialPropertyIndex index) const;
  716. template const Vector3& Material::GetPropertyValue<Vector3> (MaterialPropertyIndex index) const;
  717. template const Vector4& Material::GetPropertyValue<Vector4> (MaterialPropertyIndex index) const;
  718. template const Color& Material::GetPropertyValue<Color> (MaterialPropertyIndex index) const;
  719. template const Data::Instance<Image>& Material::GetPropertyValue<Data::Instance<Image>>(MaterialPropertyIndex index) const;
  720. const MaterialPropertyFlags& Material::GetPropertyDirtyFlags() const
  721. {
  722. return m_materialProperties.GetPropertyDirtyFlags();
  723. }
  724. RHI::ConstPtr<MaterialPropertiesLayout> Material::GetMaterialPropertiesLayout() const
  725. {
  726. return m_materialProperties.GetMaterialPropertiesLayout();
  727. }
  728. Data::Instance<RPI::ShaderResourceGroup> Material::GetShaderResourceGroup()
  729. {
  730. return m_shaderResourceGroup;
  731. }
  732. } // namespace RPI
  733. } // namespace AZ