MaterialSystem.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  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/Material/Material.h>
  9. #include <Atom/RPI.Public/Material/MaterialInstanceHandler.h>
  10. #include <Atom/RPI.Public/Material/MaterialSystem.h>
  11. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  12. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  13. #include <Atom/RPI.Reflect/Material/LuaMaterialFunctor.h>
  14. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  15. #include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
  16. #include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
  17. #include <AtomCore/Instance/InstanceDatabase.h>
  18. #include <AzCore/Name/NameDictionary.h>
  19. #include <Atom_RPI_Traits_Platform.h>
  20. #ifndef AZ_TRAITS_SCENE_MATERIALS_MAX_SAMPLERS
  21. #define AZ_TRAITS_SCENE_MATERIALS_MAX_SAMPLERS 0
  22. #endif
  23. // enable this if you want debug-prints whenever a material-Instance is registered
  24. // #define DEBUG_MATERIALINSTANCES
  25. namespace AZ::RPI
  26. {
  27. void MaterialSystem::Reflect(AZ::ReflectContext* context)
  28. {
  29. MaterialPropertyValue::Reflect(context);
  30. MaterialTypeAsset::Reflect(context);
  31. MaterialAsset::Reflect(context);
  32. MaterialPropertiesLayout::Reflect(context);
  33. MaterialFunctor::Reflect(context);
  34. MaterialNameContext::Reflect(context);
  35. LuaMaterialFunctor::Reflect(context);
  36. ReflectMaterialDynamicMetadata(context);
  37. }
  38. void MaterialSystem::GetAssetHandlers(AssetHandlerPtrList& assetHandlers)
  39. {
  40. assetHandlers.emplace_back(MakeAssetHandler<MaterialTypeAssetHandler>());
  41. assetHandlers.emplace_back(MakeAssetHandler<MaterialAssetHandler>());
  42. }
  43. bool MaterialSystem::LoadMaterialSrgShaderAsset()
  44. {
  45. if (!m_sceneMaterialSrgShaderAsset)
  46. {
  47. // Load the dummy shader containing the SceneMaterialSrg
  48. const AZStd::string materialSrgShader = "shaders/scenematerialsrg.azshader";
  49. m_sceneMaterialSrgShaderAsset =
  50. AssetUtils::LoadCriticalAsset<RPI::ShaderAsset>(materialSrgShader.data(), RPI::AssetUtils::TraceLevel::Warning);
  51. }
  52. if (!m_sceneMaterialSrgShaderAsset)
  53. {
  54. AZ_Warning("MaterialSystem", false, "Unable to locate the Material SRG shader asset, try again");
  55. return false;
  56. }
  57. CreateSceneMaterialSrg();
  58. AZ::Data::AssetBus::Handler::BusConnect(m_sceneMaterialSrgShaderAsset.GetId());
  59. return true;
  60. }
  61. void MaterialSystem::CreateSceneMaterialSrg()
  62. {
  63. if (m_sceneMaterialSrgShaderAsset->IsReady())
  64. {
  65. m_sceneMaterialSrg = ShaderResourceGroup::Create(m_sceneMaterialSrgShaderAsset, AZ_NAME_LITERAL("SceneMaterialSrg"));
  66. // get the size of the m_samplers[] array from the SRG layout
  67. auto samplerIndex = m_sceneMaterialSrg->GetLayout()->FindShaderInputSamplerIndex(AZ_NAME_LITERAL("m_samplers"));
  68. if (samplerIndex.IsValid())
  69. {
  70. auto desc = m_sceneMaterialSrg->GetLayout()->GetShaderInput(samplerIndex);
  71. [[maybe_unused]] uint32_t maxTextureSamplerStates = desc.m_count;
  72. AZ_Assert(
  73. maxTextureSamplerStates >= m_sceneTextureSamplers.GetMaxNumSamplerStates(),
  74. "SceneMaterialSrg::m_samplers[] has size %d, expected size is AZ_TRAITS_SCENE_MATERIALS_MAX_SAMPLERS (%d)",
  75. maxTextureSamplerStates,
  76. AZ_TRAITS_SCENE_MATERIALS_MAX_SAMPLERS);
  77. m_sharedSamplerStatesDirty = true;
  78. }
  79. }
  80. }
  81. // Data::AssetBus Interface
  82. void MaterialSystem::OnAssetReloaded([[maybe_unused]] AZ::Data::Asset<AZ::Data::AssetData> asset)
  83. {
  84. CreateSceneMaterialSrg();
  85. }
  86. void MaterialSystem::OnAssetReady([[maybe_unused]] AZ::Data::Asset<AZ::Data::AssetData> asset)
  87. {
  88. CreateSceneMaterialSrg();
  89. }
  90. int32_t MaterialSystem::RegisterMaterialTexture(
  91. [[maybe_unused]] const int materialTypeIndex,
  92. [[maybe_unused]] const int materialInstanceIndex,
  93. [[maybe_unused]] Data::Instance<Image> image)
  94. {
  95. int32_t textureIndex{ -1 };
  96. #ifdef AZ_TRAIT_REGISTER_TEXTURES_PER_MATERIAL
  97. if (!image)
  98. {
  99. return textureIndex;
  100. }
  101. auto& materialTypeData = m_materialTypeData[materialTypeIndex];
  102. auto& instanceData = materialTypeData.m_instanceData[materialInstanceIndex];
  103. if (instanceData.m_materialTextureRegistry)
  104. {
  105. textureIndex = instanceData.m_materialTextureRegistry->RegisterMaterialTexture(image);
  106. // we only need to update the material-textures if we actually register a new texture
  107. instanceData.m_materialTexturesDirty = true;
  108. }
  109. #endif
  110. return textureIndex;
  111. }
  112. void MaterialSystem::ReleaseMaterialTexture(
  113. [[maybe_unused]] const int materialTypeIndex,
  114. [[maybe_unused]] const int materialInstanceIndex,
  115. [[maybe_unused]] int32_t textureIndex)
  116. {
  117. #ifdef AZ_TRAIT_REGISTER_TEXTURES_PER_MATERIAL
  118. auto& materialTypeData = m_materialTypeData[materialTypeIndex];
  119. auto& instanceData = materialTypeData.m_instanceData[materialInstanceIndex];
  120. if (instanceData.m_materialTextureRegistry)
  121. {
  122. instanceData.m_materialTextureRegistry->ReleaseMaterialTexture(textureIndex);
  123. }
  124. #endif
  125. }
  126. AZStd::shared_ptr<SharedSamplerState> MaterialSystem::RegisterTextureSampler(
  127. const int materialTypeIndex, const int materialInstanceIndex, const RHI::SamplerState& samplerState)
  128. {
  129. AZStd::shared_ptr<SharedSamplerState> sharedSampler = nullptr;
  130. auto& materialTypeData = m_materialTypeData[materialTypeIndex];
  131. TextureSamplerRegistry* registry;
  132. if (materialTypeData.m_useSceneMaterialSrg)
  133. {
  134. registry = &m_sceneTextureSamplers;
  135. }
  136. else
  137. {
  138. auto& materialInstanceData = materialTypeData.m_instanceData[materialInstanceIndex];
  139. registry = materialInstanceData.m_textureSamplers.get();
  140. }
  141. auto [sharedSamplerState, registered] = registry->RegisterTextureSampler(samplerState);
  142. if (materialTypeData.m_useSceneMaterialSrg && registered)
  143. {
  144. m_sharedSamplerStatesDirty = true;
  145. }
  146. return sharedSamplerState;
  147. }
  148. const RHI::SamplerState MaterialSystem::GetRegisteredTextureSampler(
  149. const int materialTypeIndex, const int materialInstanceIndex, const uint32_t samplerIndex)
  150. {
  151. auto& materialTypeData = m_materialTypeData[materialTypeIndex];
  152. TextureSamplerRegistry* registry;
  153. if (materialTypeData.m_useSceneMaterialSrg)
  154. {
  155. registry = &m_sceneTextureSamplers;
  156. }
  157. else
  158. {
  159. auto& materialInstanceData = materialTypeData.m_instanceData[materialInstanceIndex];
  160. registry = materialInstanceData.m_textureSamplers.get();
  161. }
  162. auto sharedSamplerState = registry->GetSharedSamplerState(samplerIndex);
  163. if (sharedSamplerState == nullptr)
  164. {
  165. return RHI::SamplerState{};
  166. }
  167. return sharedSamplerState->m_samplerState;
  168. }
  169. // MaterialSrgHandler Interface
  170. MaterialInstanceData MaterialSystem::RegisterMaterialInstance(const Data::Instance<Material> material)
  171. {
  172. if (!m_sceneMaterialSrgShaderAsset)
  173. {
  174. LoadMaterialSrgShaderAsset();
  175. }
  176. m_bufferReadIndicesDirty = true;
  177. int32_t materialTypeIndex{ -1 };
  178. auto materialAsset = material->GetAsset();
  179. auto materialTypeAsset = materialAsset->GetMaterialTypeAsset();
  180. // Note: We store the Material-Parameters in a single SRG, but each object gets it's own draw-item, which holds the shader
  181. // options, so we don't need to consider them. However, for raytracing or deferred approaches, where one shader shades multiple
  182. // material-instances, we probably want to different Material-IDs for different Shader Options
  183. auto materialTypeAssetIterator = m_materialTypeIndicesMap.find(materialTypeAsset.GetId());
  184. if (materialTypeAssetIterator == m_materialTypeIndicesMap.end())
  185. {
  186. materialTypeIndex = m_materialTypeIndices.Aquire();
  187. m_materialTypeData.resize(m_materialTypeIndices.MaxCount());
  188. m_materialTypeIndicesMap.insert(AZStd::make_pair(materialTypeAsset.GetId(), materialTypeIndex));
  189. MaterialTypeData& materialTypeData = m_materialTypeData[materialTypeIndex];
  190. materialTypeData.m_materialTypeAssetId = materialTypeAsset->GetId();
  191. materialTypeData.m_materialTypeAssetHint = materialTypeAsset.GetHint();
  192. materialTypeData.m_valid = true;
  193. // make sure we hold on to the MaterialShaderParameterLayout somewhere that survives a hot reload
  194. materialTypeData.m_shaderParameterLayout =
  195. AZStd::make_unique<MaterialShaderParameterLayout>(materialTypeAsset->GetMaterialShaderParameterLayout());
  196. auto srgLayout = materialTypeAsset->GetMaterialSrgLayout();
  197. if (srgLayout)
  198. {
  199. if (m_sceneMaterialSrg && m_sceneMaterialSrg->GetLayout()->GetHash() == srgLayout->GetHash())
  200. {
  201. materialTypeData.m_useSceneMaterialSrg = true;
  202. }
  203. }
  204. }
  205. else
  206. {
  207. materialTypeIndex = materialTypeAssetIterator->second;
  208. }
  209. MaterialTypeData& materialTypeData = m_materialTypeData[materialTypeIndex];
  210. auto materialInstanceIndex = materialTypeData.m_instanceIndices.Aquire();
  211. materialTypeData.m_instanceData.resize(materialTypeData.m_instanceIndices.MaxCount());
  212. auto& instanceData = materialTypeData.m_instanceData[materialInstanceIndex];
  213. instanceData.m_material = material.get();
  214. instanceData.m_compiledChangeId = Material::DEFAULT_CHANGE_ID;
  215. if (!materialTypeData.m_useSceneMaterialSrg)
  216. {
  217. auto srgLayout = materialTypeAsset->GetMaterialSrgLayout();
  218. if (srgLayout)
  219. {
  220. auto srgShaderAsset = materialTypeAsset->GetShaderAssetForMaterialSrg();
  221. instanceData.m_shaderResourceGroup = ShaderResourceGroup::Create(srgShaderAsset, srgLayout->GetName());
  222. // get the size of the m_samplers[] array from the SRG layout
  223. auto samplerIndex = instanceData.m_shaderResourceGroup->GetLayout()->FindShaderInputSamplerIndex(AZ::Name{ "m_samplers" });
  224. if (samplerIndex.IsValid())
  225. {
  226. auto desc = instanceData.m_shaderResourceGroup->GetLayout()->GetShaderInput(samplerIndex);
  227. auto defaultSampler =
  228. RHI::SamplerState::Create(RHI::FilterMode::Linear, RHI::FilterMode::Linear, RHI::AddressMode::Wrap);
  229. defaultSampler.m_anisotropyMax = 16;
  230. instanceData.m_textureSamplers = AZStd::make_unique<TextureSamplerRegistry>();
  231. instanceData.m_textureSamplers->Init(desc.m_count, defaultSampler);
  232. }
  233. #ifdef AZ_TRAIT_REGISTER_TEXTURES_PER_MATERIAL
  234. // get the size of the m_samplers[] array from the SRG layout
  235. auto materialTexturesIndex =
  236. instanceData.m_shaderResourceGroup->GetLayout()->FindShaderInputImageIndex(AZ::Name{ "m_textures" });
  237. if (materialTexturesIndex.IsValid())
  238. {
  239. auto desc = instanceData.m_shaderResourceGroup->GetLayout()->GetShaderInput(materialTexturesIndex);
  240. instanceData.m_materialTextureRegistry = AZStd::make_unique<MaterialTextureRegistry>();
  241. instanceData.m_materialTextureRegistry->Init(desc.m_count);
  242. }
  243. #endif
  244. }
  245. }
  246. else
  247. {
  248. instanceData.m_shaderResourceGroup = m_sceneMaterialSrg;
  249. }
  250. if (instanceData.m_shaderResourceGroup)
  251. {
  252. instanceData.m_shaderParameter = aznew MaterialShaderParameter(
  253. materialTypeIndex,
  254. materialInstanceIndex,
  255. materialTypeData.m_shaderParameterLayout.get(),
  256. instanceData.m_shaderResourceGroup);
  257. }
  258. else
  259. {
  260. // the material has no SRG at all, and also no shader parameters
  261. instanceData.m_shaderParameter = {};
  262. }
  263. MaterialInstanceData result{ materialTypeIndex,
  264. materialInstanceIndex,
  265. materialTypeData.m_useSceneMaterialSrg,
  266. instanceData.m_shaderResourceGroup,
  267. instanceData.m_shaderParameter };
  268. #ifdef DEBUG_MATERIALINSTANCES
  269. AZ_Printf(
  270. "MaterialSystem",
  271. "RegisterMaterialInstance: Register Type %d (%s), Instance %d (%s) (max: %d)",
  272. materialTypeIndex,
  273. materialTypeData.m_materialTypeAssetHint.c_str(),
  274. materialInstanceIndex,
  275. instanceData.m_material->GetAsset().GetHint().c_str(),
  276. materialTypeData.m_instanceIndices.MaxCount());
  277. #endif
  278. return result;
  279. }
  280. void MaterialSystem::ReleaseMaterialInstance(const MaterialInstanceData& materialInstance)
  281. {
  282. m_bufferReadIndicesDirty = true;
  283. MaterialTypeData* materialTypeData = &m_materialTypeData[materialInstance.m_materialTypeId];
  284. #ifdef DEBUG_MATERIALINSTANCES
  285. InternalMaterialInstanceData* materialInstanceData = &materialTypeData->m_instanceData[materialInstance.m_materialInstanceId];
  286. AZ_Printf(
  287. "MaterialSystem",
  288. "ReleaseMaterialInstance: Release Type %d(%s), Instance %d (%s) (max: %d)",
  289. materialInstance.m_materialTypeId,
  290. materialTypeData->m_materialTypeAssetHint.c_str(),
  291. materialInstance.m_materialInstanceId,
  292. materialInstanceData->m_material->GetAsset().GetHint().c_str(),
  293. materialTypeData->m_instanceIndices.MaxCount());
  294. #endif
  295. materialTypeData->m_instanceData[materialInstance.m_materialInstanceId] = {};
  296. materialTypeData->m_instanceIndices.Release(materialInstance.m_materialInstanceId);
  297. if (materialTypeData->m_instanceIndices.IsFullyReleased()) // no more instances of this type
  298. {
  299. m_materialTypeIndices.Release(materialInstance.m_materialTypeId);
  300. m_materialTypeIndicesMap.erase(materialTypeData->m_materialTypeAssetId);
  301. m_materialTypeData[materialInstance.m_materialTypeId] = {};
  302. m_materialTypeData[materialInstance.m_materialTypeId].m_valid = false;
  303. materialTypeData = nullptr;
  304. }
  305. if (m_materialTypeIndices.IsFullyReleased()) // no more types in general
  306. {
  307. m_materialTypeData.clear();
  308. m_materialTypeIndices.Reset();
  309. m_materialTypeIndicesMap.clear();
  310. }
  311. }
  312. void MaterialSystem::DebugPrintMaterialInstances()
  313. {
  314. #ifdef DEBUG_MATERIALINSTANCES
  315. auto readIndices = [](const AZStd::unordered_map<int, uint32_t>& indices)
  316. {
  317. AZStd::string result;
  318. if (indices.empty())
  319. {
  320. return result;
  321. }
  322. const auto deviceCount{ AZ::RHI::RHISystemInterface::Get()->GetDeviceCount() };
  323. result.reserve(20ull * deviceCount);
  324. for (auto deviceIndex{ 0 }; deviceIndex < deviceCount; ++deviceIndex)
  325. {
  326. if (!result.empty())
  327. {
  328. result += ", ";
  329. }
  330. result += AZStd::string::format("device %d: %d", deviceIndex, static_cast<int>(indices.at(deviceIndex)));
  331. }
  332. return result;
  333. };
  334. for (int materialTypeIndex = 0; materialTypeIndex < m_materialTypeData.size(); materialTypeIndex++)
  335. {
  336. auto& materialTypeEntry = m_materialTypeData[materialTypeIndex];
  337. // The material-Type-Indices and instance-indices stay constant during their lifetime, which means we can get holes in
  338. // this buffer
  339. if (!materialTypeEntry.m_valid)
  340. {
  341. AZ_Printf("MaterialSystem", " [%d] MaterialType Empty", materialTypeIndex);
  342. continue;
  343. }
  344. AZ_Printf(
  345. "MaterialSystem",
  346. "[%d] MaterialType %s, %s, device bindless read indices = [%s]",
  347. materialTypeIndex,
  348. materialTypeEntry.m_materialTypeAssetHint.c_str(),
  349. materialTypeEntry.m_useSceneMaterialSrg ? "uses SceneMaterialSrg" : "uses custom MaterialSrg",
  350. readIndices(materialTypeEntry.m_bindlessReadIndices).c_str());
  351. for (int instanceIndex = 0; instanceIndex < materialTypeEntry.m_instanceData.size(); instanceIndex++)
  352. {
  353. auto& materialInstanceEntry = materialTypeEntry.m_instanceData[instanceIndex];
  354. if (!materialInstanceEntry.m_material)
  355. {
  356. AZ_Printf("MaterialSystem", " [%d] Instance Empty", instanceIndex);
  357. continue;
  358. }
  359. if (materialInstanceEntry.m_shaderParameter)
  360. {
  361. AZ_Printf(
  362. "MaterialSystem",
  363. " [%d] Instance %s (Offset %d, size %d)",
  364. instanceIndex,
  365. materialInstanceEntry.m_material->GetAsset().GetHint().c_str(),
  366. materialInstanceEntry.m_shaderParameter->GetStructuredBufferDataSize() * instanceIndex,
  367. materialInstanceEntry.m_shaderParameter->GetStructuredBufferDataSize());
  368. }
  369. else
  370. {
  371. AZ_Printf(
  372. "MaterialSystem",
  373. " [%d] Instance %s (no parameters)",
  374. instanceIndex,
  375. materialInstanceEntry.m_material->GetAsset().GetHint().c_str());
  376. }
  377. }
  378. }
  379. #endif
  380. }
  381. void MaterialSystem::UpdateChangedMaterialParameters()
  382. {
  383. for (auto& materialTypeEntry : m_materialTypeData)
  384. {
  385. if (!materialTypeEntry.m_valid)
  386. {
  387. continue;
  388. }
  389. size_t shaderParamsSize = 0;
  390. for (auto& instanceData : materialTypeEntry.m_instanceData)
  391. {
  392. if (instanceData.m_material && instanceData.m_shaderParameter)
  393. {
  394. shaderParamsSize = instanceData.m_shaderParameter->GetStructuredBufferDataSize();
  395. break;
  396. }
  397. }
  398. if (materialTypeEntry.m_useSceneMaterialSrg)
  399. {
  400. AZ_Assert(shaderParamsSize > 0, "MaterialSystem: Material uses SceneMaterialSrg, but has no Shader Parameters");
  401. }
  402. for (int32_t instanceIndex = 0; instanceIndex < materialTypeEntry.m_instanceIndices.MaxCount(); instanceIndex++)
  403. {
  404. auto& instanceData = materialTypeEntry.m_instanceData[instanceIndex];
  405. if (instanceData.m_material && instanceData.m_material->GetCurrentChangeId() != instanceData.m_compiledChangeId)
  406. {
  407. if (materialTypeEntry.m_useSceneMaterialSrg)
  408. {
  409. auto shaderParamsData = instanceData.m_shaderParameter->GetStructuredBufferData();
  410. materialTypeEntry.m_parameterBuffer->UpdateData(
  411. shaderParamsData, shaderParamsSize, instanceIndex * shaderParamsSize);
  412. instanceData.m_compiledChangeId = instanceData.m_material->GetCurrentChangeId();
  413. // we are only changing the data of a buffer registered in the SceneMaterialSrg, no need to compile it
  414. }
  415. else if (instanceData.m_shaderResourceGroup)
  416. {
  417. // The material doesn't use the SceneMaterialSrg: make sure the custom SRG still gets compiled
  418. #ifdef AZ_TRAIT_REGISTER_TEXTURES_PER_MATERIAL
  419. if (instanceData.m_materialTexturesDirty && instanceData.m_materialTextureRegistry)
  420. {
  421. auto texturesIndex = instanceData.m_shaderResourceGroup->FindShaderInputImageIndex(AZ::Name{ "m_textures" });
  422. if (texturesIndex.IsValid())
  423. {
  424. AZStd::vector<const RHI::ImageView*> imageViews =
  425. instanceData.m_materialTextureRegistry->CollectTextureViews();
  426. instanceData.m_shaderResourceGroup->SetImageViewArray(texturesIndex, imageViews);
  427. }
  428. instanceData.m_materialTexturesDirty = false;
  429. }
  430. #endif
  431. // register the sampler array if the material requires it
  432. auto samplerIndex = instanceData.m_shaderResourceGroup->FindShaderInputSamplerIndex(AZ::Name{ "m_samplers" });
  433. if (samplerIndex.IsValid() && instanceData.m_textureSamplers)
  434. {
  435. auto samplerStates = instanceData.m_textureSamplers->CollectSamplerStates();
  436. instanceData.m_shaderResourceGroup->SetSamplerArray(
  437. samplerIndex, { samplerStates.begin(), samplerStates.end() });
  438. }
  439. instanceData.m_shaderResourceGroup->Compile();
  440. instanceData.m_compiledChangeId = instanceData.m_material->GetCurrentChangeId();
  441. }
  442. }
  443. }
  444. }
  445. }
  446. void MaterialSystem::PrepareMaterialParameterBuffers()
  447. {
  448. auto createMaterialParameterBuffer = [](const int materialTypeIndex, const size_t elementSize, const size_t numElements)
  449. {
  450. AZ::RPI::CommonBufferDescriptor desc;
  451. desc.m_elementFormat = AZ::RHI::Format::Unknown;
  452. desc.m_poolType = AZ::RPI::CommonBufferPoolType::ReadOnly;
  453. desc.m_elementSize = static_cast<uint32_t>(elementSize);
  454. desc.m_bufferName = AZStd::string::format("MaterialParameterBuffer_%d", materialTypeIndex);
  455. desc.m_byteCount = desc.m_elementSize * numElements;
  456. return AZ::RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
  457. };
  458. auto createRawBufferView = [](Data::Instance<Buffer>& buffer)
  459. {
  460. auto bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, static_cast<uint32_t>(buffer->GetBufferSize()));
  461. return buffer->GetRHIBuffer()->GetBufferView(bufferViewDescriptor);
  462. };
  463. for (int materialTypeIndex = 0; materialTypeIndex < m_materialTypeData.size(); materialTypeIndex++)
  464. {
  465. auto& materialTypeEntry = m_materialTypeData[materialTypeIndex];
  466. // The material-Type-Indices and instance-indices stay constant during their lifetime, which means we can get holes in
  467. // this buffer
  468. if (!materialTypeEntry.m_valid)
  469. {
  470. continue;
  471. }
  472. if (!materialTypeEntry.m_useSceneMaterialSrg)
  473. {
  474. continue;
  475. }
  476. // find the first valid shader parameter entry to determine the size of the MaterialParameter-Struct
  477. size_t bufferEntrySize = 0;
  478. for (auto& instanceData : materialTypeEntry.m_instanceData)
  479. {
  480. if (instanceData.m_shaderParameter)
  481. {
  482. bufferEntrySize = instanceData.m_shaderParameter->GetStructuredBufferDataSize();
  483. break;
  484. }
  485. }
  486. auto bufferSize = bufferEntrySize * materialTypeEntry.m_instanceIndices.MaxCount();
  487. // create or resize the MaterialParameter-buffer for this material-type
  488. if (materialTypeEntry.m_parameterBuffer == nullptr)
  489. {
  490. materialTypeEntry.m_parameterBuffer =
  491. createMaterialParameterBuffer(materialTypeIndex, bufferEntrySize, materialTypeEntry.m_instanceData.size());
  492. materialTypeEntry.m_parameterBufferView = createRawBufferView(materialTypeEntry.m_parameterBuffer);
  493. }
  494. else if (materialTypeEntry.m_parameterBuffer->GetBufferSize() < bufferSize)
  495. {
  496. materialTypeEntry.m_parameterBuffer->Resize(bufferSize);
  497. materialTypeEntry.m_parameterBufferView = createRawBufferView(materialTypeEntry.m_parameterBuffer);
  498. // we need to re-upload the data after a resize
  499. for (auto& instanceData : materialTypeEntry.m_instanceData)
  500. {
  501. instanceData.m_compiledChangeId = Material::DEFAULT_CHANGE_ID;
  502. }
  503. }
  504. materialTypeEntry.m_bindlessReadIndices = materialTypeEntry.m_parameterBufferView->GetBindlessReadIndex();
  505. }
  506. }
  507. bool MaterialSystem::UpdateSharedSamplerStates()
  508. {
  509. if (m_sceneMaterialSrg)
  510. {
  511. auto samplerIndex = m_sceneMaterialSrg->FindShaderInputSamplerIndex(AZ::Name{ "m_samplers" });
  512. if (samplerIndex.IsValid())
  513. {
  514. auto samplerStates = m_sceneTextureSamplers.CollectSamplerStates();
  515. if (!samplerStates.empty())
  516. {
  517. m_sceneMaterialSrg->SetSamplerArray(samplerIndex, { samplerStates.begin(), samplerStates.end() });
  518. return true;
  519. }
  520. }
  521. }
  522. return false;
  523. }
  524. bool MaterialSystem::UpdateSceneMaterialSrg()
  525. {
  526. auto createBuffer = [](const size_t numElements)
  527. {
  528. AZ::RPI::CommonBufferDescriptor desc;
  529. desc.m_elementFormat = AZ::RHI::Format::R32_UINT;
  530. desc.m_poolType = AZ::RPI::CommonBufferPoolType::ReadOnly;
  531. desc.m_elementSize = static_cast<uint32_t>(sizeof(uint32_t));
  532. desc.m_bufferName = "MaterialTypeBufferIndicesBuffer";
  533. desc.m_byteCount = desc.m_elementSize * numElements;
  534. return AZ::RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
  535. };
  536. if (m_sceneMaterialSrg)
  537. {
  538. const auto deviceCount{ AZ::RHI::RHISystemInterface::Get()->GetDeviceCount() };
  539. // init the data to upload: Material-Types without parameter buffer get a -1 read index
  540. AZStd::unordered_map<int, AZStd::vector<int32_t>> deviceBufferData;
  541. for (auto deviceIndex{ 0 }; deviceIndex < deviceCount; ++deviceIndex)
  542. {
  543. deviceBufferData[deviceIndex].resize(m_materialTypeIndices.MaxCount(), -1);
  544. }
  545. // collect the per-device read indices of the material parameter buffers
  546. for (auto materialTypeIndex{ 0 }; materialTypeIndex < m_materialTypeData.size(); materialTypeIndex++)
  547. {
  548. auto& materialTypeData = m_materialTypeData[materialTypeIndex];
  549. if (!materialTypeData.m_valid || !materialTypeData.m_useSceneMaterialSrg)
  550. {
  551. continue;
  552. }
  553. for (const auto& [device, readIndex] : materialTypeData.m_bindlessReadIndices)
  554. {
  555. deviceBufferData[device][materialTypeIndex] = static_cast<int32_t>(readIndex);
  556. }
  557. }
  558. // prepare / resize the GPU buffer
  559. auto indicesBufferSize = static_cast<uint64_t>(sizeof(int32_t) * m_materialTypeIndices.MaxCount());
  560. if (!m_materialTypeBufferIndicesBuffer)
  561. {
  562. m_materialTypeBufferIndicesBuffer = createBuffer(m_materialTypeData.size());
  563. }
  564. if (m_materialTypeBufferIndicesBuffer->GetBufferSize() < indicesBufferSize)
  565. {
  566. m_materialTypeBufferIndicesBuffer->Resize(indicesBufferSize);
  567. }
  568. // convert the map of vectors to a map of const void*
  569. AZStd::unordered_map<int, const void*> constDeviceBufferData;
  570. for (auto deviceIndex{ 0 }; deviceIndex < deviceCount; ++deviceIndex)
  571. {
  572. constDeviceBufferData[deviceIndex] = deviceBufferData[deviceIndex].data();
  573. }
  574. // upload the GPU data, with different data for each device
  575. m_materialTypeBufferIndicesBuffer->UpdateData(constDeviceBufferData, indicesBufferSize, 0);
  576. // register the buffer in the SRG and compile it
  577. m_sceneMaterialSrg->SetBuffer(m_materialTypeBufferInputIndex, m_materialTypeBufferIndicesBuffer);
  578. return true;
  579. }
  580. return false;
  581. }
  582. void MaterialSystem::Compile()
  583. {
  584. bool compileSceneMaterialSrg = false;
  585. if (m_sharedSamplerStatesDirty)
  586. {
  587. if (UpdateSharedSamplerStates())
  588. {
  589. // make sure we try again if we didn't update the samplers successfully
  590. m_sharedSamplerStatesDirty = false;
  591. compileSceneMaterialSrg = true;
  592. }
  593. }
  594. if (m_bufferReadIndicesDirty)
  595. {
  596. PrepareMaterialParameterBuffers();
  597. if (UpdateSceneMaterialSrg())
  598. {
  599. // make sure we try again if we didn't update the SceneMaterialSrg successfully
  600. m_bufferReadIndicesDirty = false;
  601. compileSceneMaterialSrg = true;
  602. }
  603. #ifdef DEBUG_MATERIALINSTANCES
  604. DebugPrintMaterialInstances();
  605. #endif
  606. }
  607. UpdateChangedMaterialParameters();
  608. if (m_sceneMaterialSrg && compileSceneMaterialSrg)
  609. {
  610. m_sceneMaterialSrg->Compile();
  611. }
  612. }
  613. void MaterialSystem::Init()
  614. {
  615. MaterialInstanceHandlerInterface::Register(this);
  616. AZ::Data::InstanceHandler<Material> handler;
  617. handler.m_createFunction = [](Data::AssetData* materialAsset)
  618. {
  619. return Material::CreateInternal(*(azrtti_cast<MaterialAsset*>(materialAsset)));
  620. };
  621. Data::InstanceDatabase<Material>::Create(azrtti_typeid<MaterialAsset>(), handler);
  622. auto defaultSampler = RHI::SamplerState::Create(RHI::FilterMode::Linear, RHI::FilterMode::Linear, RHI::AddressMode::Wrap);
  623. defaultSampler.m_anisotropyMax = 16;
  624. m_sceneTextureSamplers.Init(AZ_TRAITS_SCENE_MATERIALS_MAX_SAMPLERS, defaultSampler);
  625. }
  626. void MaterialSystem::Shutdown()
  627. {
  628. if (m_sceneMaterialSrgShaderAsset)
  629. {
  630. AZ::Data::AssetBus::Handler::BusDisconnect(m_sceneMaterialSrgShaderAsset.GetId());
  631. m_sceneMaterialSrgShaderAsset->Release();
  632. }
  633. MaterialInstanceHandlerInterface::Unregister(this);
  634. Data::InstanceDatabase<Material>::Destroy();
  635. }
  636. } // namespace AZ::RPI