AssetCatalog.cpp 77 KB

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