3
0

Material.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  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. Init(*m_materialAsset);
  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. Init(*m_materialAsset);
  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 that it would be better to check the shaderVariantId to see if that variant is relevant to this particular material before reinitializing it.
  277. // There could be hundreds or even thousands of variants for a shader, but only one of those variants will be used by any given material. So we could
  278. // get better reload performance by only reinitializing the material when a relevant shader variant is updated.
  279. //
  280. // But it isn't always possible to know the exact ShaderVariantId that this material is using. For example, some of the shader options might not be
  281. // owned by the material and could be set externally *later* in the frame (see SetSystemShaderOption). We could probably check the shader option ownership
  282. // and mask out the parts of the ShaderVariantId that aren't owned by the material, but that would be premature optimization at this point, adding
  283. // potentially unnecessary complexity. There may also be more edge cases I haven't thought of. In short, it's much safer to just reinitialize every time
  284. // this callback happens.
  285. Init(*m_materialAsset);
  286. }
  287. ///////////////////////////////////////////////////////////////////
  288. const MaterialPropertyCollection& Material::GetPropertyCollection() const
  289. {
  290. return m_materialProperties;
  291. }
  292. const MaterialPropertyValue& Material::GetPropertyValue(MaterialPropertyIndex index) const
  293. {
  294. return m_materialProperties.GetPropertyValue(index);
  295. }
  296. const AZStd::vector<MaterialPropertyValue>& Material::GetPropertyValues() const
  297. {
  298. return m_materialProperties.GetPropertyValues();
  299. }
  300. bool Material::NeedsCompile() const
  301. {
  302. return m_compiledChangeId != m_currentChangeId;
  303. }
  304. bool Material::TryApplyPropertyConnectionToShaderInput(
  305. const MaterialPropertyValue& value,
  306. const MaterialPropertyOutputId& connection,
  307. const MaterialPropertyDescriptor* propertyDescriptor)
  308. {
  309. if (connection.m_type == MaterialPropertyOutputType::ShaderInput)
  310. {
  311. if (propertyDescriptor->GetDataType() == MaterialPropertyDataType::Image)
  312. {
  313. const Data::Instance<Image>& image = value.GetValue<Data::Instance<Image>>();
  314. RHI::ShaderInputImageIndex shaderInputIndex(connection.m_itemIndex.GetIndex());
  315. m_shaderResourceGroup->SetImage(shaderInputIndex, image);
  316. }
  317. else
  318. {
  319. RHI::ShaderInputConstantIndex shaderInputIndex(connection.m_itemIndex.GetIndex());
  320. SetShaderConstant(shaderInputIndex, value);
  321. }
  322. return true;
  323. }
  324. return false;
  325. }
  326. bool Material::TryApplyPropertyConnectionToShaderOption(
  327. const MaterialPropertyValue& value,
  328. const MaterialPropertyOutputId& connection)
  329. {
  330. if (connection.m_type == MaterialPropertyOutputType::ShaderOption)
  331. {
  332. ShaderCollection::Item* shaderReference = nullptr;
  333. if (connection.m_materialPipelineName.IsEmpty())
  334. {
  335. shaderReference = &m_generalShaderCollection[connection.m_containerIndex.GetIndex()];
  336. }
  337. else
  338. {
  339. shaderReference = &m_materialPipelineData[connection.m_materialPipelineName].m_shaderCollection[connection.m_containerIndex.GetIndex()];
  340. }
  341. SetShaderOption(*shaderReference->GetShaderOptions(), ShaderOptionIndex{connection.m_itemIndex.GetIndex()}, value);
  342. return true;
  343. }
  344. return false;
  345. }
  346. bool Material::TryApplyPropertyConnectionToShaderEnable(
  347. const MaterialPropertyValue& value,
  348. const MaterialPropertyOutputId& connection)
  349. {
  350. if (connection.m_type == MaterialPropertyOutputType::ShaderEnabled)
  351. {
  352. ShaderCollection::Item* shaderReference = nullptr;
  353. if (!value.Is<bool>())
  354. {
  355. // We should never get here because MaterialTypeAssetCreator and MaterialPropertyCollection::ValidatePropertyAccess ensure the value is a bool.
  356. AZ_Assert(false, "Unsupported data type for MaterialPropertyOutputType::ShaderEnabled");
  357. return false;
  358. }
  359. if (connection.m_materialPipelineName.IsEmpty())
  360. {
  361. shaderReference = &m_generalShaderCollection[connection.m_containerIndex.GetIndex()];
  362. }
  363. else
  364. {
  365. shaderReference = &m_materialPipelineData[connection.m_materialPipelineName].m_shaderCollection[connection.m_containerIndex.GetIndex()];
  366. }
  367. shaderReference->SetEnabled(value.GetValue<bool>());
  368. return true;
  369. }
  370. return false;
  371. }
  372. bool Material::TryApplyPropertyConnectionToInternalProperty(
  373. const MaterialPropertyValue& value,
  374. const MaterialPropertyOutputId& connection)
  375. {
  376. if (connection.m_type == MaterialPropertyOutputType::InternalProperty)
  377. {
  378. m_materialPipelineData[connection.m_materialPipelineName].m_materialProperties.SetPropertyValue(MaterialPropertyIndex{connection.m_itemIndex.GetIndex()}, value);
  379. return true;
  380. }
  381. return false;
  382. }
  383. void Material::ProcessDirectConnections()
  384. {
  385. AZ_PROFILE_SCOPE(RPI, "Process direct connection");
  386. // Apply any changes to *main* material properties...
  387. for (size_t i = 0; i < m_materialProperties.GetMaterialPropertiesLayout()->GetPropertyCount(); ++i)
  388. {
  389. if (!m_materialProperties.GetPropertyDirtyFlags()[i])
  390. {
  391. continue;
  392. }
  393. MaterialPropertyIndex propertyIndex{i};
  394. const MaterialPropertyValue value = m_materialProperties.GetPropertyValue(propertyIndex);
  395. const MaterialPropertyDescriptor* propertyDescriptor =
  396. m_materialProperties.GetMaterialPropertiesLayout()->GetPropertyDescriptor(propertyIndex);
  397. for (const MaterialPropertyOutputId& connection : propertyDescriptor->GetOutputConnections())
  398. {
  399. [[maybe_unused]] bool applied =
  400. TryApplyPropertyConnectionToShaderInput(value, connection, propertyDescriptor) ||
  401. TryApplyPropertyConnectionToShaderOption(value, connection) ||
  402. TryApplyPropertyConnectionToShaderEnable(value, connection) ||
  403. TryApplyPropertyConnectionToInternalProperty(value, connection);
  404. AZ_Error(s_debugTraceName, applied, "Connections of type %s are not supported by material properties.", ToString(connection.m_type));
  405. }
  406. }
  407. }
  408. void Material::ProcessInternalDirectConnections()
  409. {
  410. AZ_PROFILE_SCOPE(RPI, "Process direct connection");
  411. // Apply any changes to *internal* material properties...
  412. for (auto& materialPipelinePair : m_materialPipelineData)
  413. {
  414. MaterialPropertyCollection& pipelineProperties = materialPipelinePair.second.m_materialProperties;
  415. const MaterialPropertiesLayout& pipelinePropertiesLayout = *materialPipelinePair.second.m_materialProperties.GetMaterialPropertiesLayout();
  416. for (size_t i = 0; i < pipelinePropertiesLayout.GetPropertyCount(); ++i)
  417. {
  418. if (!materialPipelinePair.second.m_materialProperties.GetPropertyDirtyFlags()[i])
  419. {
  420. continue;
  421. }
  422. MaterialPropertyIndex propertyIndex{i};
  423. const MaterialPropertyValue value = pipelineProperties.GetPropertyValue(propertyIndex);
  424. const MaterialPropertyDescriptor* propertyDescriptor = pipelinePropertiesLayout.GetPropertyDescriptor(propertyIndex);
  425. for (const MaterialPropertyOutputId& connection : propertyDescriptor->GetOutputConnections())
  426. {
  427. // Note that ShaderInput is not supported for internal properties. Internal properties are used exclusively for the
  428. // .materialpipeline which is not allowed to access to the MaterialSrg, only the .materialtype should know about the MaterialSrg.
  429. [[maybe_unused]] bool applied =
  430. TryApplyPropertyConnectionToShaderOption(value, connection) ||
  431. TryApplyPropertyConnectionToShaderEnable(value, connection);
  432. AZ_Error(s_debugTraceName, applied, "Connections of type %s are not supported by material pipeline properties.", ToString(connection.m_type));
  433. }
  434. }
  435. }
  436. }
  437. void Material::ProcessMaterialFunctors()
  438. {
  439. AZ_PROFILE_SCOPE(RPI, "Process material functors");
  440. MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling;
  441. // First run the "main" MaterialPipelineNone functors, which use the MaterialFunctorAPI::RuntimeContext
  442. for (const Ptr<MaterialFunctor>& functor : m_materialAsset->GetMaterialFunctors())
  443. {
  444. if (functor)
  445. {
  446. const MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies();
  447. // None covers case that the client code doesn't register material properties to dependencies,
  448. // which will later get caught in Process() when trying to access a property.
  449. if (materialPropertyDependencies.none() || functor->NeedsProcess(m_materialProperties.GetPropertyDirtyFlags()))
  450. {
  451. MaterialFunctorAPI::RuntimeContext processContext = MaterialFunctorAPI::RuntimeContext(
  452. m_materialProperties,
  453. &materialPropertyDependencies,
  454. psoHandling,
  455. m_shaderResourceGroup.get(),
  456. &m_generalShaderCollection,
  457. &m_materialPipelineData
  458. );
  459. functor->Process(processContext);
  460. }
  461. }
  462. else
  463. {
  464. // This could happen when the dll containing the functor class is missing. There will likely be more errors
  465. // preceding this one, from the serialization system when loading the material asset.
  466. AZ_Error(s_debugTraceName, false, "Material functor is null.");
  467. }
  468. }
  469. }
  470. void Material::ProcessInternalMaterialFunctors()
  471. {
  472. AZ_PROFILE_SCOPE(RPI, "Process material functors");
  473. MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling;
  474. // Then run the "pipeline" functors, which use the MaterialFunctorAPI::PipelineRuntimeContext
  475. for (auto& [materialPipelineName, materialPipeline] : m_materialAsset->GetMaterialPipelinePayloads())
  476. {
  477. MaterialPipelineState& materialPipelineData = m_materialPipelineData[materialPipelineName];
  478. for (const Ptr<MaterialFunctor>& functor : materialPipeline.m_materialFunctors)
  479. {
  480. if (functor)
  481. {
  482. const MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies();
  483. // None covers case that the client code doesn't register material properties to dependencies,
  484. // which will later get caught in Process() when trying to access a property.
  485. if (materialPropertyDependencies.none() || functor->NeedsProcess(materialPipelineData.m_materialProperties.GetPropertyDirtyFlags()))
  486. {
  487. MaterialFunctorAPI::PipelineRuntimeContext processContext = MaterialFunctorAPI::PipelineRuntimeContext(
  488. materialPipelineData.m_materialProperties,
  489. &materialPropertyDependencies,
  490. psoHandling,
  491. &materialPipelineData.m_shaderCollection
  492. );
  493. functor->Process(processContext);
  494. }
  495. }
  496. else
  497. {
  498. // This could happen when the dll containing the functor class is missing. There will likely be more errors
  499. // preceding this one, from the serialization system when loading the material asset.
  500. AZ_Error(s_debugTraceName, false, "Material functor is null.");
  501. }
  502. }
  503. }
  504. }
  505. bool Material::Compile()
  506. {
  507. AZ_PROFILE_FUNCTION(RPI);
  508. if (!NeedsCompile())
  509. {
  510. return true;
  511. }
  512. if (CanCompile())
  513. {
  514. ProcessDirectConnections();
  515. ProcessMaterialFunctors();
  516. ProcessInternalDirectConnections();
  517. ProcessInternalMaterialFunctors();
  518. m_materialProperties.ClearAllPropertyDirtyFlags();
  519. for (auto& materialPipelinePair : m_materialPipelineData)
  520. {
  521. materialPipelinePair.second.m_materialProperties.ClearAllPropertyDirtyFlags();
  522. }
  523. if (m_shaderResourceGroup)
  524. {
  525. m_shaderResourceGroup->Compile();
  526. }
  527. m_compiledChangeId = m_currentChangeId;
  528. return true;
  529. }
  530. return false;
  531. }
  532. Material::ChangeId Material::GetCurrentChangeId() const
  533. {
  534. return m_currentChangeId;
  535. }
  536. MaterialPropertyIndex Material::FindPropertyIndex(const Name& propertyId, bool* wasRenamed, Name* newName) const
  537. {
  538. if (wasRenamed)
  539. {
  540. *wasRenamed = false;
  541. }
  542. MaterialPropertyIndex index = m_materialProperties.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId);
  543. if (!index.IsValid())
  544. {
  545. Name renamedId = propertyId;
  546. if (m_materialAsset->GetMaterialTypeAsset()->ApplyPropertyRenames(renamedId))
  547. {
  548. index = m_materialProperties.GetMaterialPropertiesLayout()->FindPropertyIndex(renamedId);
  549. if (wasRenamed)
  550. {
  551. *wasRenamed = true;
  552. }
  553. if (newName)
  554. {
  555. *newName = renamedId;
  556. }
  557. AZ_Warning("Material", false,
  558. "Material property '%s' has been renamed to '%s'. Consider updating the corresponding source data.",
  559. propertyId.GetCStr(),
  560. renamedId.GetCStr());
  561. }
  562. }
  563. return index;
  564. }
  565. bool Material::SetShaderConstant(RHI::ShaderInputConstantIndex shaderInputIndex, const MaterialPropertyValue& value)
  566. {
  567. if (!value.IsValid())
  568. {
  569. AZ_Assert(false, "Empty value found for shader input index %u", shaderInputIndex.GetIndex());
  570. return false;
  571. }
  572. else if (value.Is<bool>())
  573. {
  574. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<bool>());
  575. }
  576. else if (value.Is<int32_t>())
  577. {
  578. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<int32_t>());
  579. }
  580. else if (value.Is<uint32_t>())
  581. {
  582. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<uint32_t>());
  583. }
  584. else if (value.Is<float>())
  585. {
  586. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<float>());
  587. }
  588. else if (value.Is<Vector2>())
  589. {
  590. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Vector2>());
  591. }
  592. else if (value.Is<Vector3>())
  593. {
  594. // Vector3 is actually 16 bytes, not 12, so ShaderResourceGroup::SetConstant won't work. We
  595. // have to pass the raw data instead.
  596. return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &value.GetValue<Vector3>(), 3 * sizeof(float));
  597. }
  598. else if (value.Is<Vector4>())
  599. {
  600. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Vector4>());
  601. }
  602. else if (value.Is<Color>())
  603. {
  604. auto transformedColor = AZ::RPI::TransformColor(value.GetValue<Color>(), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
  605. // Color is special because it could map to either a float3 or a float4
  606. auto descriptor = m_shaderResourceGroup->GetLayout()->GetShaderInput(shaderInputIndex);
  607. if (descriptor.m_constantByteCount == 3 * sizeof(float))
  608. {
  609. return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &transformedColor, 3 * sizeof(float));
  610. }
  611. else
  612. {
  613. return m_shaderResourceGroup->SetConstantRaw(shaderInputIndex, &transformedColor, 4 * sizeof(float));
  614. }
  615. }
  616. else if (value.Is<Data::Instance<Image>>())
  617. {
  618. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Data::Instance<Image>>());
  619. }
  620. else if (value.Is<Data::Asset<ImageAsset>>())
  621. {
  622. return m_shaderResourceGroup->SetConstant(shaderInputIndex, value.GetValue<Data::Asset<ImageAsset>>());
  623. }
  624. else
  625. {
  626. AZ_Assert(false, "Unhandled material property value type");
  627. return false;
  628. }
  629. }
  630. bool Material::SetShaderOption(ShaderOptionGroup& options, ShaderOptionIndex shaderOptionIndex, const MaterialPropertyValue& value)
  631. {
  632. if (!value.IsValid())
  633. {
  634. AZ_Assert(false, "Empty value found for shader option %u", shaderOptionIndex.GetIndex());
  635. return false;
  636. }
  637. else if (value.Is<bool>())
  638. {
  639. return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<bool>()});
  640. }
  641. else if (value.Is<int32_t>())
  642. {
  643. return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<int32_t>()});
  644. }
  645. else if (value.Is<uint32_t>())
  646. {
  647. return options.SetValue(shaderOptionIndex, ShaderOptionValue{value.GetValue<uint32_t>()});
  648. }
  649. else
  650. {
  651. AZ_Assert(false, "MaterialProperty is incorrectly mapped to a shader option. Data type is incompatible.");
  652. return false;
  653. }
  654. }
  655. template<typename Type>
  656. bool Material::SetPropertyValue(MaterialPropertyIndex index, const Type& value)
  657. {
  658. bool success = m_materialProperties.SetPropertyValue<Type>(index, value);
  659. if (success)
  660. {
  661. ++m_currentChangeId;
  662. }
  663. return success;
  664. }
  665. // Using explicit instantiation to restrict SetPropertyValue to the set of types that we support
  666. template bool Material::SetPropertyValue<bool> (MaterialPropertyIndex index, const bool& value);
  667. template bool Material::SetPropertyValue<int32_t> (MaterialPropertyIndex index, const int32_t& value);
  668. template bool Material::SetPropertyValue<uint32_t> (MaterialPropertyIndex index, const uint32_t& value);
  669. template bool Material::SetPropertyValue<float> (MaterialPropertyIndex index, const float& value);
  670. template bool Material::SetPropertyValue<Vector2> (MaterialPropertyIndex index, const Vector2& value);
  671. template bool Material::SetPropertyValue<Vector3> (MaterialPropertyIndex index, const Vector3& value);
  672. template bool Material::SetPropertyValue<Vector4> (MaterialPropertyIndex index, const Vector4& value);
  673. template bool Material::SetPropertyValue<Color> (MaterialPropertyIndex index, const Color& value);
  674. template bool Material::SetPropertyValue<Data::Instance<Image>> (MaterialPropertyIndex index, const Data::Instance<Image>& value);
  675. bool Material::SetPropertyValue(MaterialPropertyIndex propertyIndex, const MaterialPropertyValue& value)
  676. {
  677. bool success = m_materialProperties.SetPropertyValue(propertyIndex, value);
  678. if (success)
  679. {
  680. ++m_currentChangeId;
  681. }
  682. return success;
  683. }
  684. template<typename Type>
  685. const Type& Material::GetPropertyValue(MaterialPropertyIndex index) const
  686. {
  687. return m_materialProperties.GetPropertyValue<Type>(index);
  688. }
  689. // Using explicit instantiation to restrict GetPropertyValue to the set of types that we support
  690. template const bool& Material::GetPropertyValue<bool> (MaterialPropertyIndex index) const;
  691. template const int32_t& Material::GetPropertyValue<int32_t> (MaterialPropertyIndex index) const;
  692. template const uint32_t& Material::GetPropertyValue<uint32_t> (MaterialPropertyIndex index) const;
  693. template const float& Material::GetPropertyValue<float> (MaterialPropertyIndex index) const;
  694. template const Vector2& Material::GetPropertyValue<Vector2> (MaterialPropertyIndex index) const;
  695. template const Vector3& Material::GetPropertyValue<Vector3> (MaterialPropertyIndex index) const;
  696. template const Vector4& Material::GetPropertyValue<Vector4> (MaterialPropertyIndex index) const;
  697. template const Color& Material::GetPropertyValue<Color> (MaterialPropertyIndex index) const;
  698. template const Data::Instance<Image>& Material::GetPropertyValue<Data::Instance<Image>>(MaterialPropertyIndex index) const;
  699. const MaterialPropertyFlags& Material::GetPropertyDirtyFlags() const
  700. {
  701. return m_materialProperties.GetPropertyDirtyFlags();
  702. }
  703. RHI::ConstPtr<MaterialPropertiesLayout> Material::GetMaterialPropertiesLayout() const
  704. {
  705. return m_materialProperties.GetMaterialPropertiesLayout();
  706. }
  707. } // namespace RPI
  708. } // namespace AZ