3
0

ShaderVariantAsyncLoader.cpp 36 KB

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