ShaderVariantAsyncLoader.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  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/Shader/ShaderVariantAsyncLoader.h>
  9. #include <Atom/RPI.Public/Shader/Metrics/ShaderMetricsSystem.h>
  10. #include <AzCore/Component/TickBus.h>
  11. #include <Atom/RHI/Factory.h>
  12. namespace AZ
  13. {
  14. namespace RPI
  15. {
  16. void ShaderVariantAsyncLoader::Init()
  17. {
  18. m_isServiceShutdown.store(false);
  19. AZStd::thread_desc threadDesc;
  20. threadDesc.m_name = "ShaderVariantAsyncLoader";
  21. m_serviceThread = AZStd::thread(
  22. threadDesc,
  23. [this]()
  24. {
  25. this->ThreadServiceLoop();
  26. });
  27. }
  28. void ShaderVariantAsyncLoader::ThreadServiceLoop()
  29. {
  30. AZStd::unordered_set<ShaderVariantAsyncLoader::TupleShaderAssetAndShaderVariantId> newShaderVariantPendingRequests;
  31. AZStd::unordered_set<Data::AssetId> shaderVariantTreePendingRequests;
  32. AZStd::unordered_set<Data::AssetId> shaderVariantPendingRequests;
  33. while (true)
  34. {
  35. //We'll wait here until there's work to do or this service has been shutdown.
  36. {
  37. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  38. m_workCondition.wait(lock, [&]
  39. {
  40. return m_isServiceShutdown.load() ||
  41. !m_newShaderVariantPendingRequests.empty() ||
  42. !m_shaderVariantTreePendingRequests.empty() ||
  43. !m_shaderVariantPendingRequests.empty() ||
  44. !newShaderVariantPendingRequests.empty() ||
  45. !shaderVariantTreePendingRequests.empty() ||
  46. !shaderVariantPendingRequests.empty();
  47. }
  48. );
  49. }
  50. if (m_isServiceShutdown.load())
  51. {
  52. break;
  53. }
  54. {
  55. //Move pending requests to the local lists.
  56. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  57. AZStd::for_each(
  58. m_newShaderVariantPendingRequests.begin(), m_newShaderVariantPendingRequests.end(),
  59. [&](const ShaderVariantAsyncLoader::TupleShaderAssetAndShaderVariantId& tuple) {
  60. newShaderVariantPendingRequests.insert(tuple);
  61. });
  62. m_newShaderVariantPendingRequests.clear();
  63. AZStd::for_each(m_shaderVariantTreePendingRequests.begin(), m_shaderVariantTreePendingRequests.end(),
  64. [&](const Data::AssetId& assetId)
  65. {
  66. shaderVariantTreePendingRequests.insert(assetId);
  67. });
  68. m_shaderVariantTreePendingRequests.clear();
  69. AZStd::for_each(m_shaderVariantPendingRequests.begin(), m_shaderVariantPendingRequests.end(),
  70. [&](const Data::AssetId& assetId)
  71. {
  72. shaderVariantPendingRequests.insert(assetId);
  73. });
  74. m_shaderVariantPendingRequests.clear();
  75. }
  76. // Time to work hard.
  77. auto tupleItor = newShaderVariantPendingRequests.begin();
  78. while (tupleItor != newShaderVariantPendingRequests.end())
  79. {
  80. auto shaderVariantTreeAsset = GetShaderVariantTreeAsset(tupleItor->m_shaderAsset.GetId());
  81. if (shaderVariantTreeAsset)
  82. {
  83. AZ_Assert(shaderVariantTreeAsset.IsReady(), "shaderVariantTreeAsset is not ready!");
  84. // Get the stableId from the variant tree.
  85. auto searchResult = shaderVariantTreeAsset->FindVariantStableId(
  86. tupleItor->m_shaderAsset->GetShaderOptionGroupLayout(), tupleItor->m_shaderVariantId);
  87. if (searchResult.IsRoot())
  88. {
  89. tupleItor = newShaderVariantPendingRequests.erase(tupleItor);
  90. continue;
  91. }
  92. // Record the request for metrics.
  93. ShaderMetricsSystem::Get()->RequestShaderVariant(tupleItor->m_shaderAsset.Get(), tupleItor->m_shaderVariantId, searchResult);
  94. uint32_t shaderVariantProductSubId = ShaderVariantAsset::MakeAssetProductSubId(
  95. RHI::Factory::Get().GetAPIUniqueIndex(), tupleItor->m_supervariantIndex.GetIndex(), searchResult.GetStableId());
  96. Data::AssetId shaderVariantAssetId(shaderVariantTreeAsset.GetId().m_guid, shaderVariantProductSubId);
  97. shaderVariantPendingRequests.insert(shaderVariantAssetId);
  98. tupleItor = newShaderVariantPendingRequests.erase(tupleItor);
  99. continue;
  100. }
  101. // If we are here the shaderVariantTreeAsset is not ready, but maybe it is already queued for loading,
  102. // but we try to queue it anyways.
  103. QueueShaderVariantTreeForLoading(*tupleItor, shaderVariantTreePendingRequests);
  104. tupleItor++;
  105. }
  106. auto variantTreeItor = shaderVariantTreePendingRequests.begin();
  107. while (variantTreeItor != shaderVariantTreePendingRequests.end())
  108. {
  109. if (TryToLoadShaderVariantTreeAsset(*variantTreeItor))
  110. {
  111. variantTreeItor = shaderVariantTreePendingRequests.erase(variantTreeItor);
  112. }
  113. else
  114. {
  115. variantTreeItor++;
  116. }
  117. }
  118. auto variantItor = shaderVariantPendingRequests.begin();
  119. while (variantItor != shaderVariantPendingRequests.end())
  120. {
  121. if (TryToLoadShaderVariantAsset(*variantItor))
  122. {
  123. variantItor = shaderVariantPendingRequests.erase(variantItor);
  124. }
  125. else
  126. {
  127. variantItor++;
  128. }
  129. }
  130. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1000));
  131. }
  132. }
  133. void ShaderVariantAsyncLoader::Shutdown()
  134. {
  135. if (m_isServiceShutdown.load())
  136. {
  137. return;
  138. }
  139. {
  140. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  141. m_isServiceShutdown.store(true);
  142. }
  143. m_workCondition.notify_one();
  144. m_serviceThread.join();
  145. Data::AssetBus::MultiHandler::BusDisconnect();
  146. m_newShaderVariantPendingRequests.clear();
  147. m_shaderVariantTreePendingRequests.clear();
  148. m_shaderVariantPendingRequests.clear();
  149. m_shaderVariantData.clear();
  150. m_shaderAssetIdToShaderVariantTreeAssetId.clear();
  151. }
  152. ///////////////////////////////////////////////////////////////////
  153. // IShaderVariantFinder overrides
  154. bool ShaderVariantAsyncLoader::QueueLoadShaderVariantAssetByVariantId(
  155. Data::Asset<ShaderAsset> shaderAsset, const ShaderVariantId& shaderVariantId, SupervariantIndex supervariantIndex)
  156. {
  157. if (m_isServiceShutdown.load())
  158. {
  159. return false;
  160. }
  161. {
  162. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  163. TupleShaderAssetAndShaderVariantId tuple = {shaderAsset, shaderVariantId, supervariantIndex};
  164. m_newShaderVariantPendingRequests.push_back(tuple);
  165. }
  166. m_workCondition.notify_one();
  167. return true;
  168. }
  169. bool ShaderVariantAsyncLoader::QueueLoadShaderVariantAsset(
  170. const Data::AssetId& shaderVariantTreeAssetId, ShaderVariantStableId variantStableId, SupervariantIndex supervariantIndex)
  171. {
  172. if (m_isServiceShutdown.load())
  173. {
  174. return false;
  175. }
  176. AZ_Assert(variantStableId != RootShaderVariantStableId, "Root Variants Are Found inside ShaderAssets");
  177. uint32_t shaderVariantProductSubId =
  178. ShaderVariantAsset::MakeAssetProductSubId(RHI::Factory::Get().GetAPIUniqueIndex(), supervariantIndex.GetIndex(), variantStableId);
  179. Data::AssetId shaderVariantAssetId(shaderVariantTreeAssetId.m_guid, shaderVariantProductSubId);
  180. {
  181. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  182. m_shaderVariantPendingRequests.push_back(shaderVariantAssetId);
  183. }
  184. m_workCondition.notify_one();
  185. return true;
  186. }
  187. bool ShaderVariantAsyncLoader::QueueLoadShaderVariantTreeAsset(const Data::AssetId& shaderAssetId)
  188. {
  189. if (m_isServiceShutdown.load())
  190. {
  191. return false;
  192. }
  193. {
  194. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  195. m_shaderVariantTreePendingRequests.push_back(shaderAssetId);
  196. }
  197. m_workCondition.notify_one();
  198. return true;
  199. }
  200. Data::Asset<ShaderVariantAsset> ShaderVariantAsyncLoader::GetShaderVariantAssetByVariantId(
  201. Data::Asset<ShaderAsset> shaderAsset, const ShaderVariantId& shaderVariantId, SupervariantIndex supervariantIndex)
  202. {
  203. Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset = GetShaderVariantTreeAsset(shaderAsset.GetId());
  204. if (!shaderVariantTreeAsset)
  205. {
  206. return {};
  207. }
  208. // Find the stable id.
  209. ShaderVariantSearchResult searchResult =
  210. shaderVariantTreeAsset->FindVariantStableId(shaderAsset->GetShaderOptionGroupLayout(), shaderVariantId);
  211. if (searchResult.IsRoot())
  212. {
  213. return shaderAsset->GetRootVariant();
  214. }
  215. // Record the request for metrics.
  216. ShaderMetricsSystem::Get()->RequestShaderVariant(shaderAsset.Get(), shaderVariantId, searchResult);
  217. return GetShaderVariantAsset(shaderVariantTreeAsset.GetId(), searchResult.GetStableId(), supervariantIndex);
  218. }
  219. Data::Asset<ShaderVariantAsset> ShaderVariantAsyncLoader::GetShaderVariantAssetByStableId(
  220. Data::Asset<ShaderAsset> shaderAsset, ShaderVariantStableId shaderVariantStableId, SupervariantIndex supervariantIndex)
  221. {
  222. AZ_Assert(shaderVariantStableId != RootShaderVariantStableId, "Root Variants Are Found inside ShaderAssets");
  223. Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset = GetShaderVariantTreeAsset(shaderAsset.GetId());
  224. if (!shaderVariantTreeAsset)
  225. {
  226. return {};
  227. }
  228. return GetShaderVariantAsset(shaderVariantTreeAsset.GetId(), shaderVariantStableId, supervariantIndex);
  229. }
  230. Data::Asset<ShaderVariantTreeAsset> ShaderVariantAsyncLoader::GetShaderVariantTreeAsset(const Data::AssetId& shaderAssetId)
  231. {
  232. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  233. auto assetIdFindIt = m_shaderAssetIdToShaderVariantTreeAssetId.find(shaderAssetId);
  234. if (assetIdFindIt == m_shaderAssetIdToShaderVariantTreeAssetId.end())
  235. {
  236. return {};
  237. }
  238. const Data::AssetId& shaderVariantTreeAssetId = assetIdFindIt->second;
  239. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  240. if (findIt == m_shaderVariantData.end())
  241. {
  242. return {};
  243. }
  244. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  245. if (shaderVariantCollection.m_shaderVariantTree.IsReady())
  246. {
  247. return shaderVariantCollection.m_shaderVariantTree;
  248. }
  249. return {};
  250. }
  251. Data::Asset<ShaderVariantAsset> ShaderVariantAsyncLoader::GetShaderVariantAsset(
  252. const Data::AssetId& shaderVariantTreeAssetId, ShaderVariantStableId variantStableId, SupervariantIndex supervariantIndex)
  253. {
  254. AZ_Assert(variantStableId != RootShaderVariantStableId, "Root Variants Are Found inside ShaderAssets");
  255. uint32_t shaderVariantProductSubId =
  256. ShaderVariantAsset::MakeAssetProductSubId(RHI::Factory::Get().GetAPIUniqueIndex(), supervariantIndex.GetIndex(), variantStableId);
  257. Data::AssetId shaderVariantAssetId(shaderVariantTreeAssetId.m_guid, shaderVariantProductSubId);
  258. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  259. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  260. if (findIt == m_shaderVariantData.end())
  261. {
  262. return {};
  263. }
  264. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  265. auto& shaderVariantsMap = shaderVariantCollection.m_shaderVariantsMap;
  266. auto variantFindIt = shaderVariantsMap.find(shaderVariantAssetId);
  267. if (variantFindIt == shaderVariantsMap.end())
  268. {
  269. return {};
  270. }
  271. Data::Asset<ShaderVariantAsset> shaderVariantAsset = variantFindIt->second;
  272. if (shaderVariantAsset.IsReady())
  273. {
  274. return shaderVariantAsset;
  275. }
  276. return {};
  277. }
  278. void ShaderVariantAsyncLoader::Reset()
  279. {
  280. Shutdown();
  281. Init();
  282. }
  283. ///////////////////////////////////////////////////////////////////
  284. ///////////////////////////////////////////////////////////////////////
  285. // AZ::Data::AssetBus::Handler overrides
  286. void ShaderVariantAsyncLoader::OnAssetReady(Data::Asset<Data::AssetData> asset)
  287. {
  288. //Cast it to our known asset types.
  289. if (asset.GetType() == AZ::AzTypeInfo<ShaderVariantTreeAsset>::Uuid())
  290. {
  291. OnShaderVariantTreeAssetReady(Data::static_pointer_cast<ShaderVariantTreeAsset>(asset));
  292. return;
  293. }
  294. if (asset.GetType() == AZ::AzTypeInfo<ShaderVariantAsset>::Uuid())
  295. {
  296. OnShaderVariantAssetReady(Data::static_pointer_cast<ShaderVariantAsset>(asset));
  297. return;
  298. }
  299. AZ_Error(LogName, false, "Got OnAssetReady for unknown asset type with id=%s and hint=%s\n", asset.GetId().ToString<AZStd::string>().c_str(), asset.GetHint().c_str());
  300. }
  301. void ShaderVariantAsyncLoader::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
  302. {
  303. return OnAssetReady(asset);
  304. }
  305. void ShaderVariantAsyncLoader::OnAssetError(Data::Asset<Data::AssetData> asset)
  306. {
  307. Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
  308. //Cast it to our known asset types.
  309. if (asset.GetType() == AZ::AzTypeInfo<ShaderVariantTreeAsset>::Uuid())
  310. {
  311. OnShaderVariantTreeAssetError(Data::static_pointer_cast<ShaderVariantTreeAsset>(asset));
  312. return;
  313. }
  314. if (asset.GetType() == AZ::AzTypeInfo<ShaderVariantAsset>::Uuid())
  315. {
  316. OnShaderVariantAssetError(Data::static_pointer_cast<ShaderVariantAsset>(asset));
  317. return;
  318. }
  319. AZ_Error(LogName, false, "Got OnAssetError for unknown asset type with id=%s and hint=%s\n", asset.GetId().ToString<AZStd::string>().c_str(), asset.GetHint().c_str());
  320. }
  321. ///////////////////////////////////////////////////////////////////////
  322. void ShaderVariantAsyncLoader::OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset)
  323. {
  324. // Will be used to address the notification bus.
  325. Data::AssetId shaderAssetId;
  326. {
  327. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  328. auto findIt = m_shaderVariantData.find(shaderVariantTreeAsset.GetId());
  329. if (findIt != m_shaderVariantData.end())
  330. {
  331. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  332. shaderAssetId = shaderVariantCollection.m_shaderAssetId;
  333. shaderVariantCollection.m_shaderVariantTree = shaderVariantTreeAsset;
  334. }
  335. else
  336. {
  337. AZ_Assert(false, "Was expecting a ShaderVariantCollection for shader variant tree asset with id=%s and hint=%s",
  338. shaderVariantTreeAsset.GetId().ToString<AZStd::string>().c_str(), shaderVariantTreeAsset.GetHint().c_str());
  339. return;
  340. }
  341. }
  342. AZ::TickBus::QueueFunction([shaderAssetId, shaderVariantTreeAsset]()
  343. {
  344. ShaderVariantFinderNotificationBus::Event(
  345. shaderAssetId,
  346. &ShaderVariantFinderNotification::OnShaderVariantTreeAssetReady, shaderVariantTreeAsset, false /*isError*/);
  347. });
  348. }
  349. void ShaderVariantAsyncLoader::OnShaderVariantAssetReady(Data::Asset<ShaderVariantAsset> shaderVariantAsset)
  350. {
  351. // Will be used to address the notification bus.
  352. Data::AssetId shaderAssetId;
  353. {
  354. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  355. Data::AssetId shaderVariantTreeAssetId(shaderVariantAsset.GetId().m_guid, 0);
  356. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  357. if (findIt != m_shaderVariantData.end())
  358. {
  359. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  360. shaderAssetId = shaderVariantCollection.m_shaderAssetId;
  361. auto& shaderVariantMap = shaderVariantCollection.m_shaderVariantsMap;
  362. shaderVariantMap.emplace(shaderVariantAsset.GetId(), shaderVariantAsset);
  363. }
  364. else
  365. {
  366. AZ_Assert(false, "Was expecting a ShaderVariantCollection for shader variant asset with id=%s and hint=%s",
  367. shaderVariantAsset.GetId().ToString<AZStd::string>().c_str(), shaderVariantAsset.GetHint().c_str());
  368. return;
  369. }
  370. }
  371. AZ::TickBus::QueueFunction([shaderAssetId, shaderVariantAsset]()
  372. {
  373. ShaderVariantFinderNotificationBus::Event(
  374. shaderAssetId,
  375. &ShaderVariantFinderNotification::OnShaderVariantAssetReady, shaderVariantAsset, false /*isError*/);
  376. });
  377. }
  378. void ShaderVariantAsyncLoader::OnShaderVariantTreeAssetError(Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset)
  379. {
  380. // Will be used to address the notification bus.
  381. Data::AssetId shaderAssetId;
  382. {
  383. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  384. auto findIt = m_shaderVariantData.find(shaderVariantTreeAsset.GetId());
  385. if (findIt != m_shaderVariantData.end())
  386. {
  387. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  388. shaderAssetId = shaderVariantCollection.m_shaderAssetId;
  389. m_shaderVariantData.erase(findIt);
  390. }
  391. else
  392. {
  393. AZ_Warning(LogName, false, "Was expecting a ShaderVariantCollection for shader variant tree asset with id=%s and hint=%s",
  394. shaderVariantTreeAsset.GetId().ToString<AZStd::string>().c_str(), shaderVariantTreeAsset.GetHint().c_str());
  395. return;
  396. }
  397. }
  398. AZ::TickBus::QueueFunction([shaderAssetId, shaderVariantTreeAsset]()
  399. {
  400. ShaderVariantFinderNotificationBus::Event(
  401. shaderAssetId,
  402. &ShaderVariantFinderNotification::OnShaderVariantTreeAssetReady, shaderVariantTreeAsset, true /*isError*/);
  403. });
  404. }
  405. void ShaderVariantAsyncLoader::OnShaderVariantAssetError(Data::Asset<ShaderVariantAsset> shaderVariantAsset)
  406. {
  407. // Will be used to address the notification bus.
  408. Data::AssetId shaderAssetId;
  409. {
  410. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  411. Data::AssetId shaderVariantTreeAssetId(shaderVariantAsset.GetId().m_guid, 0);
  412. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  413. if (findIt != m_shaderVariantData.end())
  414. {
  415. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  416. shaderAssetId = shaderVariantCollection.m_shaderAssetId;
  417. auto& shaderVariantMap = shaderVariantCollection.m_shaderVariantsMap;
  418. auto shaderVariantFindIt = shaderVariantMap.find(shaderVariantAsset.GetId());
  419. if (shaderVariantFindIt != shaderVariantMap.end())
  420. {
  421. shaderVariantMap.erase(shaderVariantFindIt);
  422. }
  423. }
  424. }
  425. AZ::TickBus::QueueFunction([shaderAssetId, shaderVariantAsset]()
  426. {
  427. ShaderVariantFinderNotificationBus::Event(
  428. shaderAssetId,
  429. &ShaderVariantFinderNotification::OnShaderVariantAssetReady, shaderVariantAsset, true /*isError*/);
  430. });
  431. }
  432. void ShaderVariantAsyncLoader::QueueShaderVariantTreeForLoading(
  433. const TupleShaderAssetAndShaderVariantId& shaderAndVariantTuple,
  434. AZStd::unordered_set<Data::AssetId>& shaderVariantTreePendingRequests)
  435. {
  436. auto shaderAssetId = shaderAndVariantTuple.m_shaderAsset.GetId();
  437. if (shaderVariantTreePendingRequests.count(shaderAndVariantTuple.m_shaderAsset.GetId()))
  438. {
  439. // Already queued.
  440. return;
  441. }
  442. Data::AssetId shaderVariantTreeAssetId = ShaderVariantTreeAsset::GetShaderVariantTreeAssetIdFromShaderAssetId(shaderAssetId);
  443. if (!shaderVariantTreeAssetId.IsValid())
  444. {
  445. shaderVariantTreePendingRequests.insert(shaderAssetId);
  446. return;
  447. }
  448. {
  449. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  450. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  451. if (findIt != m_shaderVariantData.end())
  452. {
  453. // Already queued.
  454. return;
  455. }
  456. }
  457. shaderVariantTreePendingRequests.insert(shaderAssetId);
  458. }
  459. bool ShaderVariantAsyncLoader::TryToLoadShaderVariantTreeAsset(const Data::AssetId& shaderAssetId)
  460. {
  461. Data::AssetId shaderVariantTreeAssetId = ShaderVariantTreeAsset::GetShaderVariantTreeAssetIdFromShaderAssetId(shaderAssetId);
  462. if (!shaderVariantTreeAssetId.IsValid())
  463. {
  464. // Return false in hope to retry later.
  465. return false;
  466. }
  467. //If we already loaded such asset let's simply dispatch the notification on the next tick.
  468. Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset;
  469. {
  470. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  471. m_shaderAssetIdToShaderVariantTreeAssetId.emplace(shaderAssetId, shaderVariantTreeAssetId);
  472. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  473. if (findIt != m_shaderVariantData.end())
  474. {
  475. shaderVariantTreeAsset = findIt->second.m_shaderVariantTree;
  476. }
  477. }
  478. if (shaderVariantTreeAsset.IsReady())
  479. {
  480. AZ::TickBus::QueueFunction([shaderAssetId, shaderVariantTreeAsset]()
  481. {
  482. ShaderVariantFinderNotificationBus::Event(
  483. shaderAssetId,
  484. &ShaderVariantFinderNotification::OnShaderVariantTreeAssetReady, shaderVariantTreeAsset, false /*isError*/);
  485. });
  486. return true;
  487. }
  488. Data::AssetBus::MultiHandler::BusDisconnect(shaderVariantTreeAssetId);
  489. //Let's queue the asset for loading.
  490. shaderVariantTreeAsset = Data::AssetManager::Instance().GetAsset<AZ::RPI::ShaderVariantTreeAsset>(shaderVariantTreeAssetId, AZ::Data::AssetLoadBehavior::QueueLoad);
  491. if (shaderVariantTreeAsset.IsError())
  492. {
  493. // The asset doesn't exist in the database yet. Return false in hope to retry later.
  494. return false;
  495. }
  496. // We need to preserve a reference to shaderVariantTreeAsset, otherwise the asset load will be cancelled.
  497. // This means we have to create the ShaderVariantCollection struct before OnAssetReady is called.
  498. {
  499. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  500. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  501. if (findIt != m_shaderVariantData.end())
  502. {
  503. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  504. shaderVariantCollection.m_shaderVariantTree = shaderVariantTreeAsset;
  505. shaderVariantCollection.m_shaderAssetId = shaderAssetId;
  506. }
  507. else
  508. {
  509. m_shaderVariantData.emplace(shaderVariantTreeAssetId, ShaderVariantCollection());
  510. ShaderVariantCollection& collection = m_shaderVariantData[shaderVariantTreeAssetId];
  511. collection.m_shaderAssetId = shaderAssetId;
  512. collection.m_shaderVariantTree = shaderVariantTreeAsset;
  513. }
  514. }
  515. Data::AssetBus::MultiHandler::BusConnect(shaderVariantTreeAssetId);
  516. return true;
  517. }
  518. bool ShaderVariantAsyncLoader::TryToLoadShaderVariantAsset(const Data::AssetId& shaderVariantAssetId)
  519. {
  520. // Will be used to address the notification bus.
  521. Data::AssetId shaderAssetId;
  522. //If we already loaded such asset let's simply dispatch the notification on the next tick.
  523. Data::Asset<ShaderVariantAsset> shaderVariantAsset;
  524. Data::AssetId shaderVariantTreeAssetId(shaderVariantAssetId.m_guid, 0);
  525. {
  526. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  527. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  528. if (findIt != m_shaderVariantData.end())
  529. {
  530. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  531. shaderAssetId = shaderVariantCollection.m_shaderAssetId;
  532. auto& shaderVariantMap = shaderVariantCollection.m_shaderVariantsMap;
  533. auto variantFindIt = shaderVariantMap.find(shaderVariantAssetId);
  534. if (variantFindIt != shaderVariantMap.end())
  535. {
  536. shaderVariantAsset = variantFindIt->second;
  537. }
  538. }
  539. else
  540. {
  541. AZ_Assert(false, "Looking for a variant without a tree.");
  542. return true; //Returning true means the requested asset should be removed from the queue.
  543. }
  544. }
  545. if (shaderVariantAsset.IsReady())
  546. {
  547. AZ::TickBus::QueueFunction([shaderAssetId, shaderVariantAsset]()
  548. {
  549. ShaderVariantFinderNotificationBus::Event(
  550. shaderAssetId,
  551. &ShaderVariantFinderNotification::OnShaderVariantAssetReady, shaderVariantAsset, false /*isError*/);
  552. });
  553. return true;
  554. }
  555. Data::AssetBus::MultiHandler::BusDisconnect(shaderVariantAssetId);
  556. // Make sure the asset actually exists
  557. Data::AssetInfo assetInfo;
  558. Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &Data::AssetCatalogRequestBus::Events::GetAssetInfoById, shaderVariantAssetId);
  559. if (!assetInfo.m_assetId.IsValid())
  560. {
  561. // The asset doesn't exist in the database yet. Return false to retry later.
  562. return false;
  563. }
  564. // Let's queue the asset for loading.
  565. shaderVariantAsset = Data::AssetManager::Instance().GetAsset<AZ::RPI::ShaderVariantAsset>(shaderVariantAssetId, AZ::Data::AssetLoadBehavior::QueueLoad);
  566. if (shaderVariantAsset.IsError())
  567. {
  568. // The asset exists (we just checked GetAssetInfoById above) but some error occurred.
  569. return false;
  570. }
  571. // We need to preserve a reference to shaderVariantAsset, otherwise the asset load will be cancelled.
  572. {
  573. AZStd::unique_lock<decltype(m_mutex)> lock(m_mutex);
  574. auto findIt = m_shaderVariantData.find(shaderVariantTreeAssetId);
  575. if (findIt != m_shaderVariantData.end())
  576. {
  577. ShaderVariantCollection& shaderVariantCollection = findIt->second;
  578. auto& shaderVariantMap = shaderVariantCollection.m_shaderVariantsMap;
  579. shaderVariantMap.emplace(shaderVariantAssetId, shaderVariantAsset);
  580. }
  581. else
  582. {
  583. AZ_Assert(false, "Looking for a variant without a tree.");
  584. return true; //Returning true means the requested asset should be removed from the queue.
  585. }
  586. }
  587. Data::AssetBus::MultiHandler::BusConnect(shaderVariantAssetId);
  588. return true;
  589. }
  590. } // namespace RPI
  591. } // namespace AZ