AssetCatalog.cpp 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816
  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 "native/AssetManager/AssetCatalog.h"
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  11. #include <AzCore/std/string/wildcard.h>
  12. #include <AzFramework/API/ApplicationAPI.h>
  13. #include <AzFramework/FileTag/FileTagBus.h>
  14. #include <AzFramework/FileTag/FileTag.h>
  15. #include <AzToolsFramework/API/AssetDatabaseBus.h>
  16. #include <QElapsedTimer>
  17. #include "PathDependencyManager.h"
  18. #include <utilities/UuidManager.h>
  19. namespace AssetProcessor
  20. {
  21. AssetCatalog::AssetCatalog(QObject* parent, AssetProcessor::PlatformConfiguration* platformConfiguration)
  22. : QObject(parent)
  23. , m_platformConfig(platformConfiguration)
  24. , m_registryBuiltOnce(false)
  25. , m_registriesMutex(QMutex::Recursive)
  26. {
  27. for (const AssetBuilderSDK::PlatformInfo& info : m_platformConfig->GetEnabledPlatforms())
  28. {
  29. if (info.m_identifier == AssetBuilderSDK::CommonPlatformName)
  30. {
  31. // Currently the Common platform is not supported as a product asset platform
  32. continue;
  33. }
  34. m_platforms.push_back(QString::fromUtf8(info.m_identifier.c_str()));
  35. }
  36. [[maybe_unused]] bool computedCacheRoot = AssetUtilities::ComputeProjectCacheRoot(m_cacheRoot);
  37. AZ_Assert(computedCacheRoot, "Could not compute cache root for AssetCatalog");
  38. // save 30mb for this. Really large projects do get this big (and bigger)
  39. // if you don't do this, things get fragmented very fast.
  40. m_saveBuffer.reserve(1024 * 1024 * 30);
  41. AssetUtilities::ComputeProjectPath();
  42. if (!ConnectToDatabase())
  43. {
  44. AZ_Error("AssetCatalog", false, "Failed to connect to sqlite database");
  45. }
  46. AssetRegistryRequestBus::Handler::BusConnect();
  47. AzToolsFramework::AssetSystemRequestBus::Handler::BusConnect();
  48. AzToolsFramework::ToolsAssetSystemBus::Handler::BusConnect();
  49. AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
  50. }
  51. AssetCatalog::~AssetCatalog()
  52. {
  53. AzToolsFramework::ToolsAssetSystemBus::Handler::BusDisconnect();
  54. AzToolsFramework::AssetSystemRequestBus::Handler::BusDisconnect();
  55. AssetRegistryRequestBus::Handler::BusDisconnect();
  56. AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
  57. SaveRegistry_Impl();
  58. }
  59. void AssetCatalog::OnAssetMessage(AzFramework::AssetSystem::AssetNotificationMessage message)
  60. {
  61. using namespace AzFramework::AssetSystem;
  62. if (message.m_type == AssetNotificationMessage::AssetChanged)
  63. {
  64. //get the full product path to determine file size
  65. AZ::Data::AssetInfo assetInfo;
  66. assetInfo.m_assetId = message.m_assetId;
  67. assetInfo.m_assetType = message.m_assetType;
  68. assetInfo.m_relativePath = message.m_data.c_str();
  69. assetInfo.m_sizeBytes = message.m_sizeBytes;
  70. QString assetPlatform{ QString::fromUtf8(message.m_platform.c_str()) };
  71. AZ_Assert(assetInfo.m_assetId.IsValid(), "AssetID is not valid!!!");
  72. AZ_Assert(!assetInfo.m_relativePath.empty(), "Product path is empty");
  73. AZ_Assert(!assetPlatform.isEmpty(), "Product platform is empty");
  74. m_catalogIsDirty = true;
  75. {
  76. QMutexLocker locker(&m_registriesMutex);
  77. m_registries[message.m_platform.c_str()].RegisterAsset(assetInfo.m_assetId, assetInfo);
  78. m_registries[assetPlatform].SetAssetDependencies(message.m_assetId, message.m_dependencies);
  79. using namespace AzFramework::FileTag;
  80. // We are checking preload Dependency only for runtime assets
  81. AZStd::vector<AZStd::string> excludedTagsList = { FileTags[static_cast<unsigned int>(FileTagsIndex::EditorOnly)] };
  82. bool editorOnlyAsset = false;
  83. QueryFileTagsEventBus::EventResult(editorOnlyAsset, FileTagType::Exclude,
  84. &QueryFileTagsEventBus::Events::Match, message.m_data.c_str(), excludedTagsList);
  85. if (!editorOnlyAsset)
  86. {
  87. for (auto& productDependency : message.m_dependencies)
  88. {
  89. auto loadBehavior = AZ::Data::ProductDependencyInfo::LoadBehaviorFromFlags(productDependency.m_flags);
  90. if (loadBehavior == AZ::Data::AssetLoadBehavior::PreLoad)
  91. {
  92. m_preloadAssetList.emplace_back(AZStd::make_pair(message.m_assetId, message.m_platform.c_str()));
  93. break;
  94. }
  95. }
  96. }
  97. }
  98. if (m_registryBuiltOnce)
  99. {
  100. Q_EMIT SendAssetMessage(message);
  101. }
  102. }
  103. else if (message.m_type == AssetNotificationMessage::AssetRemoved)
  104. {
  105. QMutexLocker locker(&m_registriesMutex);
  106. QString assetPlatform{ QString::fromUtf8(message.m_platform.c_str()) };
  107. AZ_Assert(!assetPlatform.isEmpty(), "Product platform is empty");
  108. auto found = m_registries[assetPlatform].m_assetIdToInfo.find(message.m_assetId);
  109. if (found != m_registries[assetPlatform].m_assetIdToInfo.end())
  110. {
  111. m_catalogIsDirty = true;
  112. m_registries[assetPlatform].UnregisterAsset(message.m_assetId);
  113. if (m_registryBuiltOnce)
  114. {
  115. Q_EMIT SendAssetMessage(message);
  116. }
  117. }
  118. }
  119. }
  120. bool AssetCatalog::CheckValidatedAssets(AZ::Data::AssetId assetId, const QString& platform)
  121. {
  122. auto found = m_cachedNoPreloadDependenyAssetList.equal_range(assetId);
  123. for (auto platformIter = found.first; platformIter != found.second; ++platformIter)
  124. {
  125. if (platformIter->second == platform)
  126. {
  127. // we have already verified this asset for this run and it does not have any preload dependency for the specified platform, therefore we can safely skip it
  128. return false;
  129. }
  130. }
  131. return true;
  132. }
  133. void AssetCatalog::ValidatePreLoadDependency()
  134. {
  135. if (m_currentlyValidatingPreloadDependency)
  136. {
  137. return;
  138. }
  139. m_currentlyValidatingPreloadDependency = true;
  140. for (auto iter = m_preloadAssetList.begin(); iter != m_preloadAssetList.end(); iter++)
  141. {
  142. if (!CheckValidatedAssets(iter->first, iter->second))
  143. {
  144. continue;
  145. }
  146. AZStd::stack<AZStd::pair<AZ::Data::AssetId, AZ::Data::AssetId>> assetStack;
  147. AZStd::vector<AZ::Data::AssetId> currentAssetTree; // this is used to determine the hierarchy of asset loads.
  148. AZStd::unordered_set<AZ::Data::AssetId> currentVisitedAssetsTree;
  149. AZStd::unordered_set<AZ::Data::AssetId> allVisitedAssets;
  150. assetStack.push(AZStd::make_pair(iter->first, AZ::Data::AssetId()));
  151. bool cyclicDependencyFound = false;
  152. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  153. while (!assetStack.empty())
  154. {
  155. AZ::Data::AssetId assetId = assetStack.top().first;
  156. AZ::Data::AssetId parentAssetId = assetStack.top().second;
  157. assetStack.pop();
  158. allVisitedAssets.insert(assetId);
  159. while (currentAssetTree.size() && parentAssetId != currentAssetTree.back())
  160. {
  161. currentVisitedAssetsTree.erase(currentAssetTree.back());
  162. currentAssetTree.pop_back();
  163. };
  164. currentVisitedAssetsTree.insert(assetId);
  165. currentAssetTree.emplace_back(assetId);
  166. m_db->QueryProductDependencyBySourceGuidSubId(assetId.m_guid, assetId.m_subId, iter->second.toUtf8().constData(), [&](const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)
  167. {
  168. auto loadBehavior = AZ::Data::ProductDependencyInfo::LoadBehaviorFromFlags(entry.m_dependencyFlags);
  169. if (loadBehavior == AZ::Data::AssetLoadBehavior::PreLoad)
  170. {
  171. AZ::Data::AssetId dependentAssetId(entry.m_dependencySourceGuid, entry.m_dependencySubID);
  172. if (currentVisitedAssetsTree.find(dependentAssetId) == currentVisitedAssetsTree.end())
  173. {
  174. if (!CheckValidatedAssets(dependentAssetId, iter->second))
  175. {
  176. // we have already verified that this asset does not have any preload dependency
  177. return true;
  178. }
  179. assetStack.push(AZStd::make_pair(dependentAssetId, assetId));
  180. }
  181. else
  182. {
  183. cyclicDependencyFound = true;
  184. AZStd::string cyclicPreloadDependencyTreeString;
  185. for (const auto& assetIdEntry : currentAssetTree)
  186. {
  187. AzToolsFramework::AssetDatabase::ProductDatabaseEntry productDatabaseEntry;
  188. m_db->GetProductBySourceGuidSubId(assetIdEntry.m_guid, assetIdEntry.m_subId, productDatabaseEntry);
  189. cyclicPreloadDependencyTreeString = cyclicPreloadDependencyTreeString + AZStd::string::format("%s ->", productDatabaseEntry.m_productName.c_str());
  190. };
  191. AzToolsFramework::AssetDatabase::ProductDatabaseEntry productDatabaseEntry;
  192. m_db->GetProductBySourceGuidSubId(dependentAssetId.m_guid, dependentAssetId.m_subId, productDatabaseEntry);
  193. cyclicPreloadDependencyTreeString = cyclicPreloadDependencyTreeString + AZStd::string::format(" %s ", productDatabaseEntry.m_productName.c_str());
  194. AzToolsFramework::AssetDatabase::ProductDatabaseEntry productDatabaseRootEntry;
  195. m_db->GetProductBySourceGuidSubId(iter->first.m_guid, iter->first.m_subId, productDatabaseRootEntry);
  196. AZ_Error(AssetProcessor::ConsoleChannel, false, "Preload circular dependency detected while processing asset (%s).\n Preload hierarchy is %s . Adjust your product dependencies for assets in this chain to break this loop.",
  197. productDatabaseRootEntry.m_productName.c_str(), cyclicPreloadDependencyTreeString.c_str());
  198. return false;
  199. }
  200. }
  201. return true;
  202. });
  203. if (cyclicDependencyFound)
  204. {
  205. currentVisitedAssetsTree.clear();
  206. currentAssetTree.clear();
  207. AZStd::stack<AZStd::pair<AZ::Data::AssetId, AZ::Data::AssetId>> emptyAssetStack;
  208. assetStack.swap(emptyAssetStack);
  209. }
  210. };
  211. if (!cyclicDependencyFound)
  212. {
  213. for (const auto& assetId : allVisitedAssets)
  214. {
  215. m_cachedNoPreloadDependenyAssetList.emplace(AZStd::make_pair(assetId, iter->second)); // assetid, platform
  216. }
  217. }
  218. }
  219. m_preloadAssetList.clear();
  220. m_cachedNoPreloadDependenyAssetList.clear();
  221. m_currentlyValidatingPreloadDependency = false;
  222. }
  223. void AssetCatalog::SaveRegistry_Impl()
  224. {
  225. bool allCatalogsSaved = true;
  226. // note that its safe not to save the catalog if the catalog is not dirty
  227. // because the engine will be accepting updates as long as the update has a higher or equal
  228. // number to the saveId, not just equal.
  229. if (m_catalogIsDirty)
  230. {
  231. m_catalogIsDirty = false;
  232. // Reflect registry for serialization.
  233. AZ::SerializeContext* serializeContext = nullptr;
  234. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  235. AZ_Assert(serializeContext, "Unable to retrieve serialize context.");
  236. if (nullptr == serializeContext->FindClassData(AZ::AzTypeInfo<AzFramework::AssetRegistry>::Uuid()))
  237. {
  238. AzFramework::AssetRegistry::ReflectSerialize(serializeContext);
  239. }
  240. // save out a catalog for each platform
  241. for (const QString& platform : m_platforms)
  242. {
  243. // Serialize out the catalog to a memory buffer, and then dump that memory buffer to stream.
  244. QElapsedTimer timer;
  245. timer.start();
  246. m_saveBuffer.clear();
  247. // allow this to grow by up to 20mb at a time so as not to fragment.
  248. // we re-use the save buffer each time to further reduce memory load.
  249. AZ::IO::ByteContainerStream<AZStd::vector<char>> catalogFileStream(&m_saveBuffer, 1024 * 1024 * 20);
  250. // these 3 lines are what writes the entire registry to the memory stream
  251. AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&catalogFileStream, *serializeContext, AZ::ObjectStream::ST_BINARY);
  252. {
  253. QMutexLocker locker(&m_registriesMutex);
  254. objStream->WriteClass(&m_registries[platform]);
  255. }
  256. objStream->Finalize();
  257. // now write the memory stream out to the temp folder
  258. QString workSpace;
  259. if (!AssetUtilities::CreateTempWorkspace(workSpace))
  260. {
  261. AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to create a temp workspace for catalog writing\n");
  262. }
  263. else
  264. {
  265. auto settingsRegistry = AZ::SettingsRegistry::Get();
  266. AZ::SettingsRegistryInterface::FixedValueString cacheRootFolder;
  267. settingsRegistry->Get(cacheRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder);
  268. QString tempRegistryFile = QString("%1/%2").arg(workSpace).arg("assetcatalog.xml.tmp");
  269. QString platformCacheDir = QString("%1/%2").arg(cacheRootFolder.c_str()).arg(platform);
  270. QString actualRegistryFile = QString("%1/%2").arg(platformCacheDir).arg("assetcatalog.xml");
  271. AZ_TracePrintf(AssetProcessor::DebugChannel, "Creating asset catalog: %s --> %s\n", tempRegistryFile.toUtf8().constData(), actualRegistryFile.toUtf8().constData());
  272. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  273. if (AZ::IO::FileIOBase::GetInstance()->Open(tempRegistryFile.toUtf8().data(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, fileHandle))
  274. {
  275. AZ::IO::FileIOBase::GetInstance()->Write(fileHandle, m_saveBuffer.data(), m_saveBuffer.size());
  276. AZ::IO::FileIOBase::GetInstance()->Close(fileHandle);
  277. // Make sure that the destination folder of the registry file exists
  278. QDir registryDir(platformCacheDir);
  279. if (!registryDir.exists())
  280. {
  281. QString absPath = registryDir.absolutePath();
  282. [[maybe_unused]] AZ::IO::Result makeDirResult = AZ::IO::FileIOBase::GetInstance()->CreatePath(absPath.toUtf8().constData());
  283. AZ_Warning(AssetProcessor::ConsoleChannel, makeDirResult, "Failed create folder %s", platformCacheDir.toUtf8().constData());
  284. }
  285. // if we succeeded in doing this, then use "rename" to move the file over the previous copy.
  286. bool moved = AssetUtilities::MoveFileWithTimeout(tempRegistryFile, actualRegistryFile, 3);
  287. allCatalogsSaved = allCatalogsSaved && moved;
  288. // warn if it failed
  289. AZ_Warning(AssetProcessor::ConsoleChannel, moved, "Failed to move %s to %s", tempRegistryFile.toUtf8().constData(), actualRegistryFile.toUtf8().constData());
  290. if (moved)
  291. {
  292. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Saved %s catalog containing %u assets in %fs\n", platform.toUtf8().constData(), m_registries[platform].m_assetIdToInfo.size(), timer.elapsed() / 1000.0f);
  293. }
  294. }
  295. else
  296. {
  297. AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to create catalog file %s", tempRegistryFile.toUtf8().constData());
  298. allCatalogsSaved = false;
  299. }
  300. AZ::IO::FileIOBase::GetInstance()->DestroyPath(workSpace.toUtf8().data());
  301. }
  302. }
  303. }
  304. {
  305. // scoped to minimize the duration of this mutex lock
  306. QMutexLocker locker(&m_savingRegistryMutex);
  307. m_currentlySavingCatalog = false;
  308. RegistrySaveComplete(m_currentRegistrySaveVersion, allCatalogsSaved);
  309. AssetRegistryNotificationBus::Broadcast(&AssetRegistryNotifications::OnRegistrySaveComplete, m_currentRegistrySaveVersion, allCatalogsSaved);
  310. }
  311. }
  312. AzFramework::AssetSystem::GetUnresolvedDependencyCountsResponse AssetCatalog::HandleGetUnresolvedDependencyCountsRequest(MessageData<AzFramework::AssetSystem::GetUnresolvedDependencyCountsRequest> messageData)
  313. {
  314. AzFramework::AssetSystem::GetUnresolvedDependencyCountsResponse response;
  315. {
  316. QMutexLocker locker(&m_registriesMutex);
  317. const auto& productDependencies = m_registries[messageData.m_platform].GetAssetDependencies(messageData.m_message->m_assetId);
  318. for (const AZ::Data::ProductDependency& productDependency : productDependencies)
  319. {
  320. if (m_registries[messageData.m_platform].m_assetIdToInfo.find(productDependency.m_assetId)
  321. == m_registries[messageData.m_platform].m_assetIdToInfo.end())
  322. {
  323. ++response.m_unresolvedAssetIdReferences;
  324. }
  325. }
  326. }
  327. {
  328. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  329. m_db->QueryProductDependencyBySourceGuidSubId(messageData.m_message->m_assetId.m_guid, messageData.m_message->m_assetId.m_subId, messageData.m_platform.toUtf8().constData(), [&response](const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)
  330. {
  331. if (!entry.m_unresolvedPath.empty() && entry.m_unresolvedPath.find('*') == entry.m_unresolvedPath.npos
  332. && !entry.m_unresolvedPath.starts_with(ExcludedDependenciesSymbol))
  333. {
  334. ++response.m_unresolvedPathReferences;
  335. }
  336. return true;
  337. });
  338. }
  339. return response;
  340. }
  341. void AssetCatalog::HandleSaveAssetCatalogRequest(MessageData<AzFramework::AssetSystem::SaveAssetCatalogRequest> messageData)
  342. {
  343. int registrySaveVersion = SaveRegistry();
  344. m_queuedSaveCatalogRequest.insert(registrySaveVersion, messageData.m_key);
  345. }
  346. void AssetCatalog::RegistrySaveComplete(int assetCatalogVersion, bool allCatalogsSaved)
  347. {
  348. for (auto iter = m_queuedSaveCatalogRequest.begin(); iter != m_queuedSaveCatalogRequest.end();)
  349. {
  350. if (iter.key() <= assetCatalogVersion)
  351. {
  352. AssetProcessor::NetworkRequestID& requestId = iter.value();
  353. AzFramework::AssetSystem::SaveAssetCatalogResponse saveCatalogResponse;
  354. saveCatalogResponse.m_saved = allCatalogsSaved;
  355. AssetProcessor::ConnectionBus::Event(requestId.first, &AssetProcessor::ConnectionBus::Events::SendResponse, requestId.second, saveCatalogResponse);
  356. iter = m_queuedSaveCatalogRequest.erase(iter);
  357. }
  358. else
  359. {
  360. ++iter;
  361. }
  362. }
  363. }
  364. int AssetCatalog::SaveRegistry()
  365. {
  366. QMutexLocker locker(&m_savingRegistryMutex);
  367. if (!m_currentlySavingCatalog)
  368. {
  369. m_currentlySavingCatalog = true;
  370. QMetaObject::invokeMethod(this, "SaveRegistry_Impl", Qt::QueuedConnection);
  371. return ++m_currentRegistrySaveVersion;
  372. }
  373. return m_currentRegistrySaveVersion;
  374. }
  375. void AssetCatalog::BuildRegistry()
  376. {
  377. m_catalogIsDirty = true;
  378. m_registryBuiltOnce = true;
  379. {
  380. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  381. QMutexLocker locker(&m_registriesMutex);
  382. for (QString platform : m_platforms)
  383. {
  384. auto inserted = m_registries.insert(platform, AzFramework::AssetRegistry());
  385. AzFramework::AssetRegistry& currentRegistry = inserted.value();
  386. // list of source entries in the database that need to have their UUID updated
  387. AZStd::vector<AzToolsFramework::AssetDatabase::SourceDatabaseEntry> sourceEntriesToUpdate;
  388. QElapsedTimer timer;
  389. timer.start();
  390. auto databaseQueryCallback = [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined)
  391. {
  392. SourceAssetReference sourceAsset(combined.m_scanFolderPK, combined.m_scanFolder.c_str(), combined.m_sourceName.c_str());
  393. AZ::Data::AssetId assetId;
  394. auto* fileStateInterface = AZ::Interface<IFileStateRequests>::Get();
  395. if (!fileStateInterface)
  396. {
  397. AZ_Assert(false, "Programmer Error - IFileStateRequests interface is not available");
  398. return false;
  399. }
  400. const bool fileExists = fileStateInterface->Exists(sourceAsset.AbsolutePath().c_str());
  401. // Only try to update for files which actually exist
  402. if (fileExists)
  403. {
  404. auto canonicalUuid = AssetUtilities::GetSourceUuid(sourceAsset);
  405. if (!canonicalUuid)
  406. {
  407. AZ_Error("AssetCatalog", false, "%s", canonicalUuid.GetError().c_str());
  408. return true;
  409. }
  410. assetId = AZ::Data::AssetId(canonicalUuid.GetValue(), combined.m_subID);
  411. if (canonicalUuid.GetValue() != combined.m_sourceGuid)
  412. {
  413. // Canonical UUID does not match stored UUID, this entry needs to be updated
  414. sourceEntriesToUpdate.emplace_back(
  415. combined.m_sourceID,
  416. combined.m_scanFolderID,
  417. combined.m_sourceName.c_str(),
  418. canonicalUuid.GetValue(), // Updated UUID
  419. combined.m_analysisFingerprint.c_str());
  420. }
  421. }
  422. else
  423. {
  424. assetId = AZ::Data::AssetId(combined.m_sourceGuid, combined.m_subID);
  425. }
  426. // relative file path is gotten by removing the platform and game from the product name
  427. AZStd::string_view relativeProductPath = AssetUtilities::StripAssetPlatformNoCopy(combined.m_productName);
  428. QString fullProductPath = m_cacheRoot.absoluteFilePath(combined.m_productName.c_str());
  429. AZ::u64 productFileSize = 0;
  430. AZ::IO::FileIOBase::GetInstance()->Size(fullProductPath.toUtf8().constData(), productFileSize);
  431. AZ::Data::AssetInfo info;
  432. info.m_assetType = combined.m_assetType;
  433. info.m_relativePath = relativeProductPath;
  434. info.m_assetId = assetId;
  435. info.m_sizeBytes = productFileSize;
  436. // also register it at the legacy id(s) if its different:
  437. currentRegistry.RegisterAsset(assetId, info);
  438. return true; // see them all
  439. };
  440. m_db->QueryCombined(
  441. databaseQueryCallback,
  442. AZ::Uuid::CreateNull(),
  443. nullptr,
  444. platform.toUtf8().constData(),
  445. AzToolsFramework::AssetSystem::JobStatus::Any,
  446. true); /*we still need legacy IDs - hardly anyone else does*/
  447. auto* uuidInterface = AZ::Interface<IUuidRequests>::Get();
  448. AZ_Assert(uuidInterface, "Programmer Error - IUuidRequests is not available.");
  449. AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer productDependenciesToUpdate;
  450. m_db->QueryProductDependenciesTable(
  451. [this, &platform, uuidInterface, &productDependenciesToUpdate](AZ::Data::AssetId& assetId, AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)
  452. {
  453. if (AzFramework::StringFunc::Equal(entry.m_platform.c_str(), platform.toUtf8().data()))
  454. {
  455. // Attempt to update the dependency UUID to the canonical UUID if possible
  456. if (auto canonicalUuid = uuidInterface->GetCanonicalUuid(entry.m_dependencySourceGuid); canonicalUuid && canonicalUuid.value() != entry.m_dependencySourceGuid)
  457. {
  458. entry.m_dependencySourceGuid = canonicalUuid.value();
  459. productDependenciesToUpdate.emplace_back(entry);
  460. }
  461. m_registries[platform].RegisterAssetDependency(
  462. assetId,
  463. AZ::Data::ProductDependency{ AZ::Data::AssetId(entry.m_dependencySourceGuid, entry.m_dependencySubID),
  464. entry.m_dependencyFlags });
  465. }
  466. return true;
  467. });
  468. AzToolsFramework::AssetDatabase::SourceFileDependencyEntryContainer sourceDependenciesToUpdate;
  469. m_db->QuerySourceDependencies(
  470. [&sourceDependenciesToUpdate, &uuidInterface](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& entry)
  471. {
  472. bool update = false;
  473. // Check if the sourceGuid needs to be updated
  474. if (auto canonicalUuid = uuidInterface->GetCanonicalUuid(entry.m_sourceGuid);
  475. canonicalUuid && canonicalUuid.value() != entry.m_sourceGuid)
  476. {
  477. if (canonicalUuid.value() != entry.m_sourceGuid)
  478. {
  479. update = true;
  480. entry.m_sourceGuid = canonicalUuid.value();
  481. }
  482. }
  483. // Check if the dependency uses a UUID and if it needs to be updated
  484. if (entry.m_dependsOnSource.IsUuid())
  485. {
  486. if (auto canonicalUuid = uuidInterface->GetCanonicalUuid(entry.m_dependsOnSource.GetUuid());
  487. canonicalUuid && canonicalUuid != entry.m_dependsOnSource.GetUuid())
  488. {
  489. update = true;
  490. entry.m_dependsOnSource = AzToolsFramework::AssetDatabase::PathOrUuid(canonicalUuid.value());
  491. }
  492. }
  493. if (update)
  494. {
  495. sourceDependenciesToUpdate.emplace_back(entry);
  496. }
  497. return true; // Iterate all entries
  498. });
  499. // Update any old source UUIDs
  500. for (auto& sourceDatabaseEntry : sourceEntriesToUpdate)
  501. {
  502. m_db->SetSource(sourceDatabaseEntry);
  503. }
  504. // Update any old product dependencies
  505. for (auto& productDependencyEntry : productDependenciesToUpdate)
  506. {
  507. m_db->SetProductDependency(productDependencyEntry);
  508. }
  509. // Update any old source dependencies
  510. if (!sourceDependenciesToUpdate.empty())
  511. {
  512. m_db->RemoveSourceFileDependencies(sourceDependenciesToUpdate);
  513. m_db->SetSourceFileDependencies(sourceDependenciesToUpdate);
  514. }
  515. AZ_TracePrintf(
  516. "Catalog",
  517. "Read %u assets from database for %s in %fs\n",
  518. currentRegistry.m_assetIdToInfo.size(),
  519. platform.toUtf8().constData(),
  520. timer.elapsed() / 1000.0f);
  521. }
  522. }
  523. Q_EMIT CatalogLoaded();
  524. }
  525. void AssetCatalog::OnDependencyResolved(const AZ::Data::AssetId& assetId, const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)
  526. {
  527. QString platform(entry.m_platform.c_str());
  528. if (!m_platforms.contains(platform))
  529. {
  530. return;
  531. }
  532. AzFramework::AssetSystem::AssetNotificationMessage message;
  533. message.m_type = AzFramework::AssetSystem::AssetNotificationMessage::NotificationType::AssetChanged;
  534. // Get the existing data from registry.
  535. AZ::Data::AssetInfo assetInfo = GetAssetInfoById(assetId);
  536. message.m_data = assetInfo.m_relativePath;
  537. message.m_sizeBytes = assetInfo.m_sizeBytes;
  538. message.m_assetId = assetId;
  539. message.m_assetType = assetInfo.m_assetType;
  540. message.m_platform = entry.m_platform.c_str();
  541. // Get legacyIds from registry to put in message.
  542. AZStd::unordered_map<AZ::Data::AssetId, AZ::Data::AssetId> legacyIds;
  543. // Add the new dependency entry and get the list of all dependencies for the message.
  544. AZ::Data::ProductDependency newDependency{ AZ::Data::AssetId(entry.m_dependencySourceGuid, entry.m_dependencySubID), entry.m_dependencyFlags };
  545. {
  546. QMutexLocker locker(&m_registriesMutex);
  547. m_registries[platform].RegisterAssetDependency(assetId, newDependency);
  548. message.m_dependencies = AZStd::move(m_registries[platform].GetAssetDependencies(assetId));
  549. }
  550. if (m_registryBuiltOnce)
  551. {
  552. Q_EMIT SendAssetMessage(message);
  553. }
  554. m_catalogIsDirty = true;
  555. }
  556. void AssetCatalog::OnConnect(unsigned int connectionId, QStringList platforms)
  557. {
  558. // Send out a message for each asset to make sure the connected tools are aware of the existence of all previously built assets
  559. // since the assetcatalog might not have been written out to disk previously.
  560. for (QString platform : platforms)
  561. {
  562. QMutexLocker locker(&m_registriesMutex);
  563. auto itr = m_registries.find(platform);
  564. if (itr == m_registries.end())
  565. {
  566. continue;
  567. }
  568. const auto& currentRegistry = *itr;
  569. AzFramework::AssetSystem::BulkAssetNotificationMessage bulkMessage;
  570. bulkMessage.m_messages.reserve(currentRegistry.m_assetIdToInfo.size());
  571. bulkMessage.m_type = AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged;
  572. for (const auto& assetInfo : currentRegistry.m_assetIdToInfo)
  573. {
  574. AzFramework::AssetSystem::AssetNotificationMessage message(
  575. assetInfo.second.m_relativePath.c_str(),
  576. AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged,
  577. assetInfo.second.m_assetType,
  578. platform.toUtf8().constData());
  579. message.m_assetId = assetInfo.second.m_assetId;
  580. message.m_sizeBytes = assetInfo.second.m_sizeBytes;
  581. message.m_dependencies = AZStd::move(currentRegistry.GetAssetDependencies(assetInfo.second.m_assetId));
  582. bulkMessage.m_messages.push_back(AZStd::move(message));
  583. }
  584. AssetProcessor::ConnectionBus::Event(connectionId, &AssetProcessor::ConnectionBus::Events::Send, 0, bulkMessage);
  585. }
  586. }
  587. void AssetCatalog::OnSourceQueued(AZ::Uuid sourceUuid, AZStd::unordered_set<AZ::Uuid> legacyUuids, const SourceAssetReference& sourceAsset)
  588. {
  589. AZStd::lock_guard<AZStd::mutex> lock(m_sourceUUIDToSourceNameMapMutex);
  590. m_sourceUUIDToSourceAssetMap.insert({ sourceUuid, sourceAsset });
  591. //adding legacy source uuid as well
  592. for (const auto legacyUuid : legacyUuids)
  593. {
  594. m_sourceUUIDToSourceAssetMap.insert({ legacyUuid, sourceAsset });
  595. }
  596. m_sourceAssetToSourceUUIDMap.insert({ sourceAsset, sourceUuid });
  597. }
  598. void AssetCatalog::OnSourceFinished(AZ::Uuid sourceUuid, AZStd::unordered_set<AZ::Uuid> legacyUuids)
  599. {
  600. AZStd::lock_guard<AZStd::mutex> lock(m_sourceUUIDToSourceNameMapMutex);
  601. auto found = m_sourceUUIDToSourceAssetMap.find(sourceUuid);
  602. if (found != m_sourceUUIDToSourceAssetMap.end())
  603. {
  604. m_sourceAssetToSourceUUIDMap.erase(found->second);
  605. }
  606. m_sourceUUIDToSourceAssetMap.erase(sourceUuid);
  607. for (const auto& legacyUuid : legacyUuids)
  608. {
  609. m_sourceUUIDToSourceAssetMap.erase(legacyUuid);
  610. }
  611. }
  612. //////////////////////////////////////////////////////////////////////////
  613. bool AssetCatalog::GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullSourceOrProductPath, AZStd::string& relativeProductPath)
  614. {
  615. ProcessGetRelativeProductPathFromFullSourceOrProductPathRequest(fullSourceOrProductPath, relativeProductPath);
  616. if (!relativeProductPath.length())
  617. {
  618. // if we are here it means we have failed to determine the assetId we will send back the original path
  619. AZ_TracePrintf(AssetProcessor::DebugChannel, "GetRelativeProductPath no result, returning original %s...\n", fullSourceOrProductPath.c_str());
  620. relativeProductPath = fullSourceOrProductPath;
  621. return false;
  622. }
  623. return true;
  624. }
  625. bool AssetCatalog::GenerateRelativeSourcePath(
  626. const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFolder)
  627. {
  628. QString normalizedSourcePath = AssetUtilities::NormalizeFilePath(sourcePath.c_str());
  629. QDir inputPath(normalizedSourcePath);
  630. QString scanFolder;
  631. QString relativeName;
  632. bool validResult = false;
  633. AZ_TracePrintf(AssetProcessor::DebugChannel, "ProcessGenerateRelativeSourcePathRequest: %s...\n", sourcePath.c_str());
  634. if (sourcePath.empty())
  635. {
  636. // For an empty input path, do nothing, we'll return an empty, invalid result.
  637. // (We check fullPath instead of inputPath, because an empty fullPath actually produces "." for inputPath)
  638. }
  639. else if (inputPath.isAbsolute())
  640. {
  641. // For an absolute path, try to convert it to a relative path, based on the existing scan folders.
  642. // To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed.
  643. validResult = m_platformConfig->ConvertToRelativePath(inputPath.absolutePath(), relativeName, scanFolder);
  644. }
  645. else if (inputPath.isRelative())
  646. {
  647. // For a relative path, concatenate it with each scan folder, and see if a valid relative path emerges.
  648. int scanFolders = m_platformConfig->GetScanFolderCount();
  649. for (int scanIdx = 0; scanIdx < scanFolders; scanIdx++)
  650. {
  651. auto& scanInfo = m_platformConfig->GetScanFolderAt(scanIdx);
  652. QDir possibleRoot(scanInfo.ScanPath());
  653. QDir possibleAbsolutePath = possibleRoot.filePath(normalizedSourcePath);
  654. // To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed.
  655. if (m_platformConfig->ConvertToRelativePath(possibleAbsolutePath.absolutePath(), relativeName, scanFolder))
  656. {
  657. validResult = true;
  658. break;
  659. }
  660. }
  661. }
  662. // The input has produced a valid relative path. However, the path might match multiple nested scan folders,
  663. // so look to see if a higher-priority folder has a better match.
  664. if (validResult)
  665. {
  666. QString overridingFile = m_platformConfig->GetOverridingFile(relativeName, scanFolder);
  667. if (!overridingFile.isEmpty())
  668. {
  669. overridingFile = AssetUtilities::NormalizeFilePath(overridingFile);
  670. validResult = m_platformConfig->ConvertToRelativePath(overridingFile, relativeName, scanFolder);
  671. }
  672. }
  673. if (!validResult)
  674. {
  675. // if we are here it means we have failed to determine the relativePath, so we will send back the original path
  676. AZ_TracePrintf(AssetProcessor::DebugChannel,
  677. "GenerateRelativeSourcePath found no valid result, returning original path: %s...\n", sourcePath.c_str());
  678. rootFolder.clear();
  679. relativePath.clear();
  680. relativePath = sourcePath;
  681. return false;
  682. }
  683. relativePath = relativeName.toUtf8().data();
  684. rootFolder = scanFolder.toUtf8().data();
  685. AZ_Assert(!relativePath.empty(), "ConvertToRelativePath returned true, but relativePath is empty");
  686. return true;
  687. }
  688. bool AssetCatalog::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath)
  689. {
  690. ProcessGetFullSourcePathFromRelativeProductPathRequest(relPath, fullSourcePath);
  691. if (!fullSourcePath.length())
  692. {
  693. // if we are here it means that we failed to determine the full source path from the relative path and we will send back the original path
  694. AZ_TracePrintf(AssetProcessor::DebugChannel, "GetFullSourcePath no result, returning original %s...\n", relPath.c_str());
  695. fullSourcePath = relPath;
  696. return false;
  697. }
  698. return true;
  699. }
  700. bool AssetCatalog::GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath)
  701. {
  702. assetInfo.m_assetId.SetInvalid();
  703. assetInfo.m_relativePath.clear();
  704. assetInfo.m_assetType = AZ::Data::s_invalidAssetType;
  705. assetInfo.m_sizeBytes = 0;
  706. // If the assetType wasn't provided, try to guess it
  707. if (assetType.IsNull())
  708. {
  709. SourceAssetReference sourceAsset;
  710. bool result = GetAssetInfoByIdOnly(assetId, platformName, assetInfo, sourceAsset);
  711. rootFilePath = sourceAsset.ScanFolderPath().c_str();
  712. return result;
  713. }
  714. bool isSourceType;
  715. {
  716. AZStd::lock_guard<AZStd::mutex> lock(m_sourceAssetTypesMutex);
  717. isSourceType = m_sourceAssetTypes.find(assetType) != m_sourceAssetTypes.end();
  718. }
  719. // If the assetType is registered as a source type, look up the source info
  720. if (isSourceType)
  721. {
  722. SourceAssetReference sourceAsset;
  723. if (GetSourceFileInfoFromAssetId(assetId, sourceAsset))
  724. {
  725. assetInfo.m_assetId = assetId;
  726. assetInfo.m_assetType = assetType;
  727. assetInfo.m_relativePath = sourceAsset.RelativePath().c_str();
  728. AZ::IO::FileIOBase::GetInstance()->Size(sourceAsset.AbsolutePath().c_str(), assetInfo.m_sizeBytes);
  729. rootFilePath = sourceAsset.ScanFolderPath().c_str();
  730. return true;
  731. }
  732. return false;
  733. }
  734. // Return the product file info
  735. rootFilePath.clear(); // products don't have root file paths.
  736. assetInfo = GetProductAssetInfo(platformName.c_str(), assetId);
  737. return !assetInfo.m_relativePath.empty();
  738. }
  739. QString AssetCatalog::GetDefaultAssetPlatform()
  740. {
  741. // get the first available platform, preferring the host platform.
  742. if (m_platforms.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()))
  743. {
  744. return QString::fromUtf8(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
  745. }
  746. // the GetHostAssetPlatform() "pc" or "osx" is not actually enabled for this compilation (maybe "server" or similar is in a build job).
  747. // in that case, we'll use the first we find!
  748. return m_platforms[0];
  749. }
  750. AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> AssetCatalog::GetDirectProductDependencies(
  751. const AZ::Data::AssetId& id)
  752. {
  753. QString platform = GetDefaultAssetPlatform();
  754. QMutexLocker locker(&m_registriesMutex);
  755. auto itr = m_registries[platform].m_assetDependencies.find(id);
  756. if (itr == m_registries[platform].m_assetDependencies.end())
  757. {
  758. return AZ::Failure<AZStd::string>("Failed to find asset in dependency map");
  759. }
  760. return AZ::Success(itr->second);
  761. }
  762. AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> AssetCatalog::GetAllProductDependencies(const AZ::Data::AssetId& id)
  763. {
  764. return GetAllProductDependenciesFilter(id, {}, {});
  765. }
  766. AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> AssetCatalog::GetAllProductDependenciesFilter(
  767. const AZ::Data::AssetId& id,
  768. const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
  769. const AZStd::vector<AZStd::string>& wildcardPatternExclusionList)
  770. {
  771. AZStd::vector<AZ::Data::ProductDependency> dependencyList;
  772. AZStd::unordered_set<AZ::Data::AssetId> assetSet;
  773. AZ::Data::PreloadAssetListType preloadList;
  774. if (exclusionList.find(id) != exclusionList.end())
  775. {
  776. return AZ::Success(AZStd::move(dependencyList));
  777. }
  778. for (const AZStd::string& wildcardPattern : wildcardPatternExclusionList)
  779. {
  780. if (DoesAssetIdMatchWildcardPattern(id, wildcardPattern))
  781. {
  782. return AZ::Success(AZStd::move(dependencyList));
  783. }
  784. }
  785. AddAssetDependencies(id, assetSet, dependencyList, exclusionList, wildcardPatternExclusionList, preloadList);
  786. // dependencyList will be appended to while looping, so use a traditional loop
  787. for (size_t i = 0; i < dependencyList.size(); ++i)
  788. {
  789. AddAssetDependencies(dependencyList[i].m_assetId, assetSet, dependencyList, exclusionList, wildcardPatternExclusionList, preloadList);
  790. }
  791. return AZ::Success(AZStd::move(dependencyList));
  792. }
  793. AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> AssetCatalog::GetLoadBehaviorProductDependencies(
  794. const AZ::Data::AssetId& id, AZStd::unordered_set<AZ::Data::AssetId>& noloadSet,
  795. AZ::Data::PreloadAssetListType& preloadAssetList)
  796. {
  797. AZStd::vector<AZ::Data::ProductDependency> dependencyList;
  798. AZStd::vector<AZ::Data::ProductDependency> returnList;
  799. AZStd::unordered_set<AZ::Data::AssetId> assetSet;
  800. AddAssetDependencies(id, assetSet, dependencyList, {}, {}, preloadAssetList);
  801. // dependencyList will be appended to while looping, so use a traditional loop
  802. for (size_t i = 0; i < dependencyList.size(); ++i)
  803. {
  804. if (AZ::Data::ProductDependencyInfo::LoadBehaviorFromFlags(dependencyList[i].m_flags) == AZ::Data::AssetLoadBehavior::NoLoad)
  805. {
  806. noloadSet.insert(dependencyList[i].m_assetId);
  807. assetSet.erase(dependencyList[i].m_assetId);
  808. }
  809. else
  810. {
  811. returnList.push_back(dependencyList[i]);
  812. AddAssetDependencies(dependencyList[i].m_assetId, assetSet, dependencyList, {}, {}, preloadAssetList);
  813. }
  814. }
  815. return AZ::Success(AZStd::move(returnList));
  816. }
  817. bool AssetCatalog::DoesAssetIdMatchWildcardPattern(const AZ::Data::AssetId& assetId, const AZStd::string& wildcardPattern)
  818. {
  819. if (wildcardPattern.empty())
  820. {
  821. // pattern is empty, there is nothing to match
  822. return false;
  823. }
  824. AZStd::string relativePath = GetAssetPathById(assetId);
  825. if (relativePath.empty())
  826. {
  827. // assetId did not resolve to a relative path, cannot be matched
  828. return false;
  829. }
  830. return AZStd::wildcard_match(wildcardPattern, relativePath);
  831. }
  832. void AssetCatalog::AddAssetDependencies(
  833. const AZ::Data::AssetId& searchAssetId,
  834. AZStd::unordered_set<AZ::Data::AssetId>& assetSet,
  835. AZStd::vector<AZ::Data::ProductDependency>& dependencyList,
  836. const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
  837. const AZStd::vector<AZStd::string>& wildcardPatternExclusionList,
  838. AZ::Data::PreloadAssetListType& preloadAssetList)
  839. {
  840. using namespace AZ::Data;
  841. QString platform = GetDefaultAssetPlatform();
  842. QMutexLocker locker(&m_registriesMutex);
  843. auto itr = m_registries[platform].m_assetDependencies.find(searchAssetId);
  844. if (itr != m_registries[platform].m_assetDependencies.end())
  845. {
  846. AZStd::vector<ProductDependency>& assetDependencyList = itr->second;
  847. for (const ProductDependency& dependency : assetDependencyList)
  848. {
  849. if (!dependency.m_assetId.IsValid())
  850. {
  851. continue;
  852. }
  853. if (exclusionList.find(dependency.m_assetId) != exclusionList.end())
  854. {
  855. continue;
  856. }
  857. bool isWildcardMatch = false;
  858. for (const AZStd::string& wildcardPattern : wildcardPatternExclusionList)
  859. {
  860. isWildcardMatch = DoesAssetIdMatchWildcardPattern(dependency.m_assetId, wildcardPattern);
  861. if (isWildcardMatch)
  862. {
  863. break;
  864. }
  865. }
  866. if (isWildcardMatch)
  867. {
  868. continue;
  869. }
  870. auto loadBehavior = AZ::Data::ProductDependencyInfo::LoadBehaviorFromFlags(dependency.m_flags);
  871. if (loadBehavior == AZ::Data::AssetLoadBehavior::PreLoad)
  872. {
  873. preloadAssetList[searchAssetId].insert(dependency.m_assetId);
  874. }
  875. // Only proceed if this ID is valid and we haven't encountered this assetId before.
  876. // Invalid IDs usually come from unmet path product dependencies.
  877. if (assetSet.find(dependency.m_assetId) == assetSet.end())
  878. {
  879. assetSet.insert(dependency.m_assetId); // add to the set of already-encountered assets
  880. dependencyList.push_back(dependency); // put it in the flat list of dependencies we've found
  881. }
  882. }
  883. }
  884. }
  885. bool AssetCatalog::GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)
  886. {
  887. if (!sourcePath || strlen(sourcePath) <= 0)
  888. {
  889. assetInfo.m_assetId.SetInvalid();
  890. return false;
  891. }
  892. SourceAssetReference sourceAsset;
  893. if (!AzFramework::StringFunc::Path::IsRelative(sourcePath))
  894. {
  895. QString scanFolder;
  896. QString relPath;
  897. // Call ConvertToRelativePath first to verify the sourcePath exists in a scanfolder
  898. if (m_platformConfig->ConvertToRelativePath(sourcePath, relPath, scanFolder))
  899. {
  900. sourceAsset = SourceAssetReference(scanFolder, relPath);
  901. }
  902. }
  903. else
  904. {
  905. // relative paths get the first matching asset, and then they get the usual call.
  906. QString absolutePath = m_platformConfig->FindFirstMatchingFile(QString::fromUtf8(sourcePath));
  907. if (!absolutePath.isEmpty())
  908. {
  909. sourceAsset = SourceAssetReference(absolutePath);
  910. }
  911. }
  912. if (!sourceAsset)
  913. {
  914. assetInfo.m_assetId.SetInvalid();
  915. return false;
  916. }
  917. // now that we have a database path, we can at least return something.
  918. // but source info also includes UUID, which we need to hit the database for (or the in-memory map).
  919. // Check the database first for the UUID now that we have the "database name" (which includes output prefix)
  920. {
  921. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  922. AzToolsFramework::AssetDatabase::SourceDatabaseEntry returnedSource;
  923. if (m_db->GetSourceBySourceNameScanFolderId(sourceAsset.RelativePath().c_str(), sourceAsset.ScanFolderId(), returnedSource))
  924. {
  925. const AzToolsFramework::AssetDatabase::SourceDatabaseEntry& entry = returnedSource;
  926. AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanEntry;
  927. if (m_db->GetScanFolderByScanFolderID(entry.m_scanFolderPK, scanEntry))
  928. {
  929. watchFolder = scanEntry.m_scanFolder;
  930. // since we are returning the UUID of a source file, as opposed to the full assetId of a product file produced by that source file,
  931. // the subId part of the assetId will always be set to zero.
  932. assetInfo.m_assetId = AZ::Data::AssetId(entry.m_sourceGuid, 0);
  933. assetInfo.m_relativePath = entry.m_sourceName;
  934. AZStd::string absolutePath;
  935. AzFramework::StringFunc::Path::Join(scanEntry.m_scanFolder.c_str(), assetInfo.m_relativePath.c_str(), absolutePath);
  936. AZ::IO::FileIOBase::GetInstance()->Size(absolutePath.c_str(), assetInfo.m_sizeBytes);
  937. assetInfo.m_assetType = AZ::Uuid::CreateNull(); // most source files don't have a type!
  938. // Go through the list of source assets and see if this asset's file path matches any of the filters
  939. for (const auto& pair : m_sourceAssetTypeFilters)
  940. {
  941. if (AZStd::wildcard_match(pair.first, assetInfo.m_relativePath))
  942. {
  943. assetInfo.m_assetType = pair.second;
  944. break;
  945. }
  946. }
  947. return true;
  948. }
  949. }
  950. }
  951. watchFolder = sourceAsset.ScanFolderPath().c_str();
  952. // Source file isn't in the database yet, see if its in the job queue
  953. if (GetQueuedAssetInfoByRelativeSourceName(sourceAsset, assetInfo))
  954. {
  955. return true;
  956. }
  957. // Source file isn't in the job queue yet, source UUID needs to be created
  958. return GetUncachedSourceInfoFromDatabaseNameAndWatchFolder(sourceAsset, assetInfo);
  959. }
  960. bool AssetCatalog::GetSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)
  961. {
  962. AZ::Data::AssetId partialId(sourceUuid, 0);
  963. SourceAssetReference sourceAsset;
  964. if (GetSourceFileInfoFromAssetId(partialId, sourceAsset))
  965. {
  966. watchFolder = sourceAsset.ScanFolderPath().c_str();
  967. assetInfo.m_assetId = partialId;
  968. assetInfo.m_assetType = AZ::Uuid::CreateNull(); // most source files don't have a type!
  969. assetInfo.m_relativePath = sourceAsset.RelativePath().c_str();
  970. AZ::IO::FileIOBase::GetInstance()->Size(sourceAsset.AbsolutePath().c_str(), assetInfo.m_sizeBytes);
  971. // if the type has registered with a typeid, then supply it here
  972. AZStd::lock_guard<AZStd::mutex> lock(m_sourceAssetTypesMutex);
  973. // Go through the list of source assets and see if this asset's file path matches any of the filters
  974. // if it does, we know what type it is (if not, the above call to CreateNull ensures it is null).
  975. for (const auto& pair : m_sourceAssetTypeFilters)
  976. {
  977. if (AZStd::wildcard_match(pair.first, sourceAsset.RelativePath().c_str()))
  978. {
  979. assetInfo.m_assetType = pair.second;
  980. break;
  981. }
  982. }
  983. return true;
  984. }
  985. // failed!
  986. return false;
  987. }
  988. bool AssetCatalog::GetAssetsProducedBySourceUUID(const AZ::Uuid& sourceUuid, AZStd::vector<AZ::Data::AssetInfo>& productsAssetInfo)
  989. {
  990. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  991. AzToolsFramework::AssetDatabase::SourceDatabaseEntry entry;
  992. if (m_db->GetSourceBySourceGuid(sourceUuid, entry))
  993. {
  994. AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer products;
  995. if (m_db->GetProductsBySourceID(entry.m_sourceID, products))
  996. {
  997. for (const AzToolsFramework::AssetDatabase::ProductDatabaseEntry& product : products)
  998. {
  999. AZ::Data::AssetInfo assetInfo;
  1000. assetInfo.m_assetId = AZ::Data::AssetId(sourceUuid, product.m_subID);
  1001. assetInfo.m_assetType = product.m_assetType;
  1002. productsAssetInfo.emplace_back(assetInfo);
  1003. }
  1004. }
  1005. return true;
  1006. }
  1007. return false;
  1008. }
  1009. bool AssetCatalog::ClearFingerprintForAsset(const AZStd::string& sourcePath)
  1010. {
  1011. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  1012. SourceAssetReference sourceAsset;
  1013. if (QFileInfo(sourcePath.c_str()).isAbsolute())
  1014. {
  1015. sourceAsset = SourceAssetReference(sourcePath.c_str());
  1016. }
  1017. else
  1018. {
  1019. QString absolutePath = m_platformConfig->FindFirstMatchingFile(sourcePath.c_str());
  1020. if (absolutePath.isEmpty())
  1021. {
  1022. return false;
  1023. }
  1024. sourceAsset = SourceAssetReference(absolutePath.toUtf8().constData());
  1025. }
  1026. if(!m_db->UpdateFileHashByFileNameAndScanFolderId(sourceAsset.RelativePath().c_str(), sourceAsset.ScanFolderId(), 0))
  1027. {
  1028. return false;
  1029. }
  1030. AzToolsFramework::AssetDatabase::SourceDatabaseEntry source;
  1031. if (!m_db->GetSourceBySourceNameScanFolderId(sourceAsset.RelativePath().c_str(), sourceAsset.ScanFolderId(), source))
  1032. {
  1033. return false;
  1034. }
  1035. // if setting the file hash failed, still try to clear the job fingerprints.
  1036. return m_db->SetJobFingerprintsBySourceID(source.m_sourceID, 0);
  1037. }
  1038. bool AssetCatalog::GetScanFolders(AZStd::vector<AZStd::string>& scanFolders)
  1039. {
  1040. int scanFolderCount = m_platformConfig->GetScanFolderCount();
  1041. for (int i = 0; i < scanFolderCount; ++i)
  1042. {
  1043. scanFolders.push_back(m_platformConfig->GetScanFolderAt(i).ScanPath().toUtf8().constData());
  1044. }
  1045. return true;
  1046. }
  1047. bool AssetCatalog::GetAssetSafeFolders(AZStd::vector<AZStd::string>& assetSafeFolders)
  1048. {
  1049. int scanFolderCount = m_platformConfig->GetScanFolderCount();
  1050. for (int scanFolderIndex = 0; scanFolderIndex < scanFolderCount; ++scanFolderIndex)
  1051. {
  1052. AssetProcessor::ScanFolderInfo& scanFolder = m_platformConfig->GetScanFolderAt(scanFolderIndex);
  1053. if (scanFolder.CanSaveNewAssets())
  1054. {
  1055. assetSafeFolders.push_back(scanFolder.ScanPath().toUtf8().constData());
  1056. }
  1057. }
  1058. return true;
  1059. }
  1060. bool AssetCatalog::IsAssetPlatformEnabled(const char* platform)
  1061. {
  1062. const AZStd::vector<AssetBuilderSDK::PlatformInfo>& enabledPlatforms = m_platformConfig->GetEnabledPlatforms();
  1063. for (const AssetBuilderSDK::PlatformInfo& platformInfo : enabledPlatforms)
  1064. {
  1065. if (platformInfo.m_identifier == platform)
  1066. {
  1067. return true;
  1068. }
  1069. }
  1070. return false;
  1071. }
  1072. int AssetCatalog::GetPendingAssetsForPlatform(const char* /*platform*/)
  1073. {
  1074. AZ_Assert(false, "Call to unsupported Asset Processor function GetPendingAssetsForPlatform on AssetCatalog");
  1075. return -1;
  1076. }
  1077. AZStd::string AssetCatalog::GetAssetPathById(const AZ::Data::AssetId& id)
  1078. {
  1079. return GetAssetInfoById(id).m_relativePath;
  1080. }
  1081. AZ::Data::AssetId AssetCatalog::GetAssetIdByPath(const char* path, const AZ::Data::AssetType& typeToRegister, bool autoRegisterIfNotFound)
  1082. {
  1083. AZ_UNUSED(autoRegisterIfNotFound);
  1084. AZ_Assert(autoRegisterIfNotFound == false, "Auto registration is invalid during asset processing.");
  1085. AZ_UNUSED(typeToRegister);
  1086. AZ_Assert(typeToRegister == AZ::Data::s_invalidAssetType, "Can not register types during asset processing.");
  1087. AZStd::string relProductPath;
  1088. GetRelativeProductPathFromFullSourceOrProductPath(path, relProductPath);
  1089. QString tempPlatformName = GetDefaultAssetPlatform();
  1090. AZ::Data::AssetId assetId;
  1091. {
  1092. QMutexLocker locker(&m_registriesMutex);
  1093. assetId = m_registries[tempPlatformName].GetAssetIdByPath(relProductPath.c_str());
  1094. }
  1095. return assetId;
  1096. }
  1097. AZ::Data::AssetInfo AssetCatalog::GetAssetInfoById(const AZ::Data::AssetId& id)
  1098. {
  1099. AZ::Data::AssetType assetType;
  1100. AZ::Data::AssetInfo assetInfo;
  1101. AZStd::string rootFilePath;
  1102. GetAssetInfoById(id, assetType, "", assetInfo, rootFilePath);
  1103. return assetInfo;
  1104. }
  1105. bool ConvertDatabaseProductPathToProductFilename(AZStd::string_view dbPath, QString& productFileName)
  1106. {
  1107. // Always strip the leading directory from the product path
  1108. // The leading directory can be either an asset platform path or a subfolder
  1109. AZ::StringFunc::TokenizeNext(dbPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
  1110. if (!dbPath.empty())
  1111. {
  1112. productFileName = QString::fromUtf8(dbPath.data(), aznumeric_cast<int>(dbPath.size()));
  1113. return true;
  1114. }
  1115. return false;
  1116. }
  1117. void AssetCatalog::ProcessGetRelativeProductPathFromFullSourceOrProductPathRequest(const AZStd::string& fullPath, AZStd::string& relativeProductPath)
  1118. {
  1119. QString sourceOrProductPath = fullPath.c_str();
  1120. QString normalizedSourceOrProductPath = AssetUtilities::NormalizeFilePath(sourceOrProductPath);
  1121. QString productFileName;
  1122. bool resultCode = false;
  1123. QDir inputPath(normalizedSourceOrProductPath);
  1124. AZ_TracePrintf(AssetProcessor::DebugChannel, "ProcessGetRelativeProductPath: %s...\n", sourceOrProductPath.toUtf8().constData());
  1125. if (inputPath.isRelative())
  1126. {
  1127. //if the path coming in is already a relative path,we just send it back
  1128. productFileName = sourceOrProductPath;
  1129. resultCode = true;
  1130. }
  1131. else
  1132. {
  1133. QDir cacheRoot;
  1134. AssetUtilities::ComputeProjectCacheRoot(cacheRoot);
  1135. QString normalizedCacheRoot = AssetUtilities::NormalizeFilePath(cacheRoot.path());
  1136. if (AssetUtilities::IsInCacheFolder(normalizedSourceOrProductPath.toUtf8().constData(), cacheRoot.absolutePath().toUtf8().constData()))
  1137. {
  1138. // The path send by the game/editor contains the cache root so we try to find the asset id
  1139. // from the asset database
  1140. normalizedSourceOrProductPath.remove(0, normalizedCacheRoot.length() + 1); // adding 1 for the native separator
  1141. // If we are here it means that the asset database does not have any knowledge about this file,
  1142. // most probably because AP has not processed the file yet
  1143. // In this case we will try to compute the asset id from the product path
  1144. // Now after removing the cache root,normalizedInputAssetPath can either be $Platform/$Game/xxx/yyy or something like $Platform/zzz
  1145. // and the corresponding assetId have to be either xxx/yyy or zzz
  1146. resultCode = ConvertDatabaseProductPathToProductFilename(normalizedSourceOrProductPath.toUtf8().data(), productFileName);
  1147. }
  1148. else
  1149. {
  1150. // If we are here it means its a source file, first see whether there is any overriding file and then try to find products
  1151. QString scanFolder;
  1152. QString relativeName;
  1153. if (m_platformConfig->ConvertToRelativePath(normalizedSourceOrProductPath, relativeName, scanFolder))
  1154. {
  1155. QString overridingFile = m_platformConfig->GetOverridingFile(relativeName, scanFolder);
  1156. if (overridingFile.isEmpty())
  1157. {
  1158. // no overriding file found
  1159. overridingFile = normalizedSourceOrProductPath;
  1160. }
  1161. else
  1162. {
  1163. overridingFile = AssetUtilities::NormalizeFilePath(overridingFile);
  1164. }
  1165. const auto* scanFolderInfo = m_platformConfig->GetScanFolderForFile(overridingFile);
  1166. if (scanFolderInfo && m_platformConfig->ConvertToRelativePath(overridingFile, scanFolderInfo, relativeName))
  1167. {
  1168. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  1169. AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer products;
  1170. if (m_db->GetProductsBySourceNameScanFolderID(relativeName, scanFolderInfo->ScanFolderID(), products))
  1171. {
  1172. resultCode = ConvertDatabaseProductPathToProductFilename(products[0].m_productName, productFileName);
  1173. }
  1174. else
  1175. {
  1176. productFileName = relativeName;
  1177. resultCode = true;
  1178. }
  1179. }
  1180. }
  1181. }
  1182. }
  1183. if (!resultCode)
  1184. {
  1185. productFileName = "";
  1186. }
  1187. relativeProductPath = productFileName.toUtf8().data();
  1188. }
  1189. void AssetCatalog::ProcessGetFullSourcePathFromRelativeProductPathRequest(const AZStd::string& relPath, AZStd::string& fullSourcePath)
  1190. {
  1191. QString assetPath = relPath.c_str();
  1192. QString normalizedAssetPath = AssetUtilities::NormalizeFilePath(assetPath);
  1193. int resultCode = 0;
  1194. QString fullAssetPath;
  1195. if (normalizedAssetPath.isEmpty())
  1196. {
  1197. fullSourcePath = "";
  1198. return;
  1199. }
  1200. QDir inputPath(normalizedAssetPath);
  1201. if (inputPath.isAbsolute())
  1202. {
  1203. QDir cacheRoot;
  1204. AssetUtilities::ComputeProjectCacheRoot(cacheRoot);
  1205. QString normalizedCacheRoot = AssetUtilities::NormalizeFilePath(cacheRoot.path());
  1206. if (!AssetUtilities::IsInCacheFolder(normalizedAssetPath.toUtf8().constData(), cacheRoot.absolutePath().toUtf8().constData()))
  1207. {
  1208. // Attempt to convert to relative path
  1209. QString dummy, convertedRelPath;
  1210. if (m_platformConfig->ConvertToRelativePath(assetPath, convertedRelPath, dummy))
  1211. {
  1212. // then find the first matching file to get correct casing
  1213. fullAssetPath = m_platformConfig->FindFirstMatchingFile(convertedRelPath);
  1214. }
  1215. if (fullAssetPath.isEmpty())
  1216. {
  1217. // if we couldn't find it, just return the passed in path
  1218. fullAssetPath = assetPath;
  1219. }
  1220. resultCode = 1;
  1221. }
  1222. else
  1223. {
  1224. // The path send by the game/editor contains the cache root ,try to find the productName from it
  1225. normalizedAssetPath.remove(0, normalizedCacheRoot.length() + 1); // adding 1 for the native separator
  1226. }
  1227. }
  1228. if (!resultCode)
  1229. {
  1230. //remove aliases if present
  1231. normalizedAssetPath = AssetUtilities::NormalizeAndRemoveAlias(normalizedAssetPath);
  1232. if (!normalizedAssetPath.isEmpty()) // this happens if it comes in as just for example "@products@/"
  1233. {
  1234. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  1235. //We should have the asset now, we can now find the full asset path
  1236. // we have to check each platform individually until we get a hit.
  1237. const auto& platforms = m_platformConfig->GetEnabledPlatforms();
  1238. QString productName;
  1239. for (const AssetBuilderSDK::PlatformInfo& platformInfo : platforms)
  1240. {
  1241. if (platformInfo.m_identifier == AssetBuilderSDK::CommonPlatformName)
  1242. {
  1243. // Common platform is not supported for product assets currently
  1244. continue;
  1245. }
  1246. QString platformName = QString::fromUtf8(platformInfo.m_identifier.c_str());
  1247. productName = AssetUtilities::GuessProductNameInDatabase(normalizedAssetPath, platformName, m_db.get());
  1248. if (!productName.isEmpty())
  1249. {
  1250. break;
  1251. }
  1252. }
  1253. if (!productName.isEmpty())
  1254. {
  1255. //Now find the input name for the path,if we are here this should always return true since we were able to find the productName before
  1256. AzToolsFramework::AssetDatabase::SourceDatabaseEntryContainer sources;
  1257. if (m_db->GetSourcesByProductName(productName, sources))
  1258. {
  1259. //Once we have found the inputname we will try finding the full path
  1260. fullAssetPath = m_platformConfig->FindFirstMatchingFile(sources[0].m_sourceName.c_str());
  1261. if (!fullAssetPath.isEmpty())
  1262. {
  1263. resultCode = 1;
  1264. }
  1265. }
  1266. }
  1267. else
  1268. {
  1269. // if we are not able to guess the product name than maybe the asset path is an input name
  1270. fullAssetPath = m_platformConfig->FindFirstMatchingFile(normalizedAssetPath);
  1271. if (!fullAssetPath.isEmpty())
  1272. {
  1273. resultCode = 1;
  1274. }
  1275. }
  1276. }
  1277. }
  1278. if (!resultCode)
  1279. {
  1280. fullSourcePath = "";
  1281. }
  1282. else
  1283. {
  1284. fullSourcePath = fullAssetPath.toUtf8().data();
  1285. }
  1286. }
  1287. //////////////////////////////////////////////////////////////////////////
  1288. void AssetCatalog::RegisterSourceAssetType(const AZ::Data::AssetType& assetType, const char* assetFileFilter)
  1289. {
  1290. AZStd::lock_guard<AZStd::mutex> lock(m_sourceAssetTypesMutex);
  1291. m_sourceAssetTypes.insert(assetType);
  1292. AZStd::vector<AZStd::string> tokens;
  1293. AZStd::string semicolonSeperated(assetFileFilter);
  1294. AZStd::tokenize(semicolonSeperated, AZStd::string(";"), tokens);
  1295. for (const auto& pattern : tokens)
  1296. {
  1297. m_sourceAssetTypeFilters[pattern] = assetType;
  1298. }
  1299. }
  1300. void AssetCatalog::UnregisterSourceAssetType(const AZ::Data::AssetType& /*assetType*/)
  1301. {
  1302. // For now, this does nothing, because it would just needlessly complicate things for no gain.
  1303. // Unregister is only called when a builder is shut down, which really is only supposed to happen when AssetCatalog is being shutdown
  1304. // Without a way of tracking how many builders have registered the same assetType and being able to perfectly keep track of every builder shutdown, even in the event of a crash,
  1305. // the map would either be cleared prematurely or never get cleared at all
  1306. }
  1307. //////////////////////////////////////////////////////////////////////////
  1308. bool AssetCatalog::GetSourceFileInfoFromAssetId(const AZ::Data::AssetId &assetId, SourceAssetReference& sourceAsset)
  1309. {
  1310. // Try checking the UuidManager, it keeps track of legacy UUIDs
  1311. auto* uuidInterface = AZ::Interface<AssetProcessor::IUuidRequests>::Get();
  1312. AZ_Assert(uuidInterface, "Programmer Error - IUuidRequests interface is not available.");
  1313. if (auto result = uuidInterface->FindHighestPriorityFileByUuid(assetId.m_guid); result)
  1314. {
  1315. sourceAsset = SourceAssetReference(result.value());
  1316. return true;
  1317. }
  1318. // Check the database next
  1319. {
  1320. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  1321. AzToolsFramework::AssetDatabase::SourceDatabaseEntry entry;
  1322. if (m_db->GetSourceBySourceGuid(assetId.m_guid, entry))
  1323. {
  1324. AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanEntry;
  1325. if (m_db->GetScanFolderByScanFolderID(entry.m_scanFolderPK, scanEntry))
  1326. {
  1327. sourceAsset = SourceAssetReference(scanEntry.m_scanFolder.c_str(), entry.m_sourceName.c_str());
  1328. return true;
  1329. }
  1330. }
  1331. }
  1332. // Source file isn't in the database yet, see if its in the job queue
  1333. return GetQueuedAssetInfoById(assetId.m_guid, sourceAsset);
  1334. }
  1335. AZ::Data::AssetInfo AssetCatalog::GetProductAssetInfo(const char* platformName, const AZ::Data::AssetId& assetId)
  1336. {
  1337. // this more or less follows the same algorithm that the game uses to look up products.
  1338. using namespace AZ::Data;
  1339. using namespace AzFramework;
  1340. if ((!assetId.IsValid()) || (m_platforms.isEmpty()))
  1341. {
  1342. return AssetInfo();
  1343. }
  1344. // in case no platform name has been given, we are prepared to compute one.
  1345. QString tempPlatformName;
  1346. // if no platform specified, we'll use the current platform.
  1347. if ((!platformName) || (platformName[0] == 0))
  1348. {
  1349. tempPlatformName = GetDefaultAssetPlatform();
  1350. }
  1351. else
  1352. {
  1353. tempPlatformName = QString::fromUtf8(platformName);
  1354. }
  1355. // note that m_platforms is not mutated at all during runtime, so we ignore it in the lock
  1356. if (!m_platforms.contains(tempPlatformName))
  1357. {
  1358. return AssetInfo();
  1359. }
  1360. QMutexLocker locker(&m_registriesMutex);
  1361. const AssetRegistry& registryToUse = m_registries[tempPlatformName];
  1362. auto foundIter = registryToUse.m_assetIdToInfo.find(assetId);
  1363. if (foundIter != registryToUse.m_assetIdToInfo.end())
  1364. {
  1365. return foundIter->second;
  1366. }
  1367. return AssetInfo(); // not found!
  1368. }
  1369. bool AssetCatalog::GetAssetInfoByIdOnly(const AZ::Data::AssetId& id, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, SourceAssetReference& sourceAsset)
  1370. {
  1371. if (GetSourceFileInfoFromAssetId(id, sourceAsset))
  1372. {
  1373. {
  1374. AZStd::lock_guard<AZStd::mutex> lock(m_sourceAssetTypesMutex);
  1375. // Go through the list of source assets and see if this asset's file path matches any of the filters
  1376. for (const auto& pair : m_sourceAssetTypeFilters)
  1377. {
  1378. if (AZStd::wildcard_match(pair.first, sourceAsset.AbsolutePath().c_str()))
  1379. {
  1380. assetInfo.m_assetId = id;
  1381. assetInfo.m_assetType = pair.second;
  1382. assetInfo.m_relativePath = sourceAsset.RelativePath().c_str();
  1383. AZ::IO::FileIOBase::GetInstance()->Size(sourceAsset.AbsolutePath().c_str(), assetInfo.m_sizeBytes);
  1384. return true;
  1385. }
  1386. }
  1387. }
  1388. // If we get to here, we're going to assume it's a product type
  1389. sourceAsset = {};
  1390. assetInfo = GetProductAssetInfo(platformName.c_str(), id);
  1391. return !assetInfo.m_relativePath.empty();
  1392. }
  1393. // Asset isn't in the DB or in the APM queue, we don't know what this asset ID is
  1394. return false;
  1395. }
  1396. bool AssetCatalog::GetQueuedAssetInfoById(const AZ::Uuid& guid, SourceAssetReference& sourceAsset)
  1397. {
  1398. if (!guid.IsNull())
  1399. {
  1400. AZStd::lock_guard<AZStd::mutex> lock(m_sourceUUIDToSourceNameMapMutex);
  1401. auto foundSource = m_sourceUUIDToSourceAssetMap.find(guid);
  1402. if (foundSource != m_sourceUUIDToSourceAssetMap.end())
  1403. {
  1404. sourceAsset = foundSource->second;
  1405. return true;
  1406. }
  1407. AZ_TracePrintf(AssetProcessor::DebugChannel, "GetQueuedAssetInfoById: AssetCatalog unable to find the requested source asset having uuid (%s).\n", guid.ToString<AZStd::string>().c_str());
  1408. }
  1409. return false;
  1410. }
  1411. bool AssetCatalog::GetQueuedAssetInfoByRelativeSourceName(const SourceAssetReference& sourceAsset, AZ::Data::AssetInfo& assetInfo)
  1412. {
  1413. if (sourceAsset)
  1414. {
  1415. AZStd::lock_guard<AZStd::mutex> lock(m_sourceUUIDToSourceNameMapMutex);
  1416. auto foundSourceUUID = m_sourceAssetToSourceUUIDMap.find(sourceAsset);
  1417. if (foundSourceUUID != m_sourceAssetToSourceUUIDMap.end())
  1418. {
  1419. assetInfo.m_relativePath = sourceAsset.RelativePath().c_str();
  1420. assetInfo.m_assetId = foundSourceUUID->second;
  1421. AZ::IO::FileIOBase::GetInstance()->Size(sourceAsset.AbsolutePath().c_str(), assetInfo.m_sizeBytes);
  1422. assetInfo.m_assetType = AZ::Uuid::CreateNull(); // most source files don't have a type!
  1423. // Go through the list of source assets and see if this asset's file path matches any of the filters
  1424. for (const auto& pair : m_sourceAssetTypeFilters)
  1425. {
  1426. if (AZStd::wildcard_match(pair.first, assetInfo.m_relativePath))
  1427. {
  1428. assetInfo.m_assetType = pair.second;
  1429. break;
  1430. }
  1431. }
  1432. return true;
  1433. }
  1434. }
  1435. assetInfo.m_assetId.SetInvalid();
  1436. return false;
  1437. }
  1438. bool AssetCatalog::GetUncachedSourceInfoFromDatabaseNameAndWatchFolder(const SourceAssetReference& sourceAsset, AZ::Data::AssetInfo& assetInfo)
  1439. {
  1440. // Make sure the source file exists first
  1441. AZ::IO::FileIOBase* io = AZ::IO::FileIOBase::GetInstance();
  1442. AZ_Assert(io, "Expected a FileIO Interface");
  1443. if (!io->Exists(sourceAsset.AbsolutePath().c_str()))
  1444. {
  1445. return false;
  1446. }
  1447. auto sourceUUID = AssetUtilities::GetSourceUuid(sourceAsset);
  1448. if (!sourceUUID)
  1449. {
  1450. return false;
  1451. }
  1452. AZ::Data::AssetId sourceAssetId(sourceUUID.GetValue(), 0);
  1453. assetInfo.m_assetId = sourceAssetId;
  1454. assetInfo.m_relativePath = sourceAsset.RelativePath().c_str();
  1455. io->Size(sourceAsset.AbsolutePath().c_str(), assetInfo.m_sizeBytes);
  1456. assetInfo.m_assetType = AZ::Uuid::CreateNull();
  1457. // Go through the list of source assets and see if this asset's file path matches any of the filters
  1458. for (const auto& pair : m_sourceAssetTypeFilters)
  1459. {
  1460. if (AZStd::wildcard_match(pair.first, assetInfo.m_relativePath))
  1461. {
  1462. assetInfo.m_assetType = pair.second;
  1463. break;
  1464. }
  1465. }
  1466. return true;
  1467. }
  1468. bool AssetCatalog::ConnectToDatabase()
  1469. {
  1470. AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
  1471. if (!m_db)
  1472. {
  1473. AZStd::string databaseLocation;
  1474. AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Broadcast(&AzToolsFramework::AssetDatabase::AssetDatabaseRequests::GetAssetDatabaseLocation, databaseLocation);
  1475. if (!databaseLocation.empty())
  1476. {
  1477. m_db = AZStd::make_unique<AssetProcessor::AssetDatabaseConnection>();
  1478. m_db->OpenDatabase();
  1479. return true;
  1480. }
  1481. return false;
  1482. }
  1483. return true;
  1484. }
  1485. void AssetCatalog::AsyncAssetCatalogStatusRequest()
  1486. {
  1487. if (m_catalogIsDirty)
  1488. {
  1489. Q_EMIT AsyncAssetCatalogStatusResponse(AssetCatalogStatus::RequiresSaving);
  1490. }
  1491. else
  1492. {
  1493. Q_EMIT AsyncAssetCatalogStatusResponse(AssetCatalogStatus::UpToDate);
  1494. }
  1495. }
  1496. }//namespace AssetProcessor