ApplicationManagerBase.cpp 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141
  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 "ApplicationManagerBase.h"
  9. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  10. #include <AzCore/std/smart_ptr/make_shared.h>
  11. #include <AzCore/std/sort.h>
  12. #include <native/assetprocessor.h>
  13. #include <native/utilities/BuilderConfigurationManager.h>
  14. #include <native/resourcecompiler/rccontroller.h>
  15. #include <native/AssetManager/assetScanner.h>
  16. #include <native/AssetManager/FileStateCache.h>
  17. #include <native/AssetManager/ControlRequestHandler.h>
  18. #include <native/connection/connectionManager.h>
  19. #include <native/utilities/ByteArrayStream.h>
  20. #include <native/AssetManager/AssetRequestHandler.h>
  21. #include <native/FileProcessor/FileProcessor.h>
  22. #include <native/FileWatcher/FileWatcher.h>
  23. #include <native/utilities/ApplicationServer.h>
  24. #include <native/utilities/AssetServerHandler.h>
  25. #include <native/InternalBuilders/SettingsRegistryBuilder.h>
  26. #include <AzToolsFramework/Application/Ticker.h>
  27. #include <AzToolsFramework/ToolsFileUtils/ToolsFileUtils.h>
  28. #include <AssetBuilder/AssetBuilderStatic.h>
  29. #include <iostream>
  30. #include <QCoreApplication>
  31. #include <QElapsedTimer>
  32. //! CreateJobs will wait up to 2 minutes before timing out
  33. //! This shouldn't need to be so high but very large slices can take a while to process currently
  34. //! This should be reduced down to something more reasonable after slice jobs are sped up
  35. static const int s_MaximumCreateJobsTimeSeconds = 60 * 2;
  36. //! ProcessJobs will wait up to 1 hour before timing out
  37. static const int s_MaximumProcessJobsTimeSeconds = 60 * 60;
  38. //! Reserve extra disk space when doing disk space checks to leave a little room for logging, database operations, etc
  39. static const qint64 s_ReservedDiskSpaceInBytes = 256 * 1024;
  40. //! Maximum number of temp folders allowed
  41. static const int s_MaximumTempFolders = 10000;
  42. ApplicationManagerBase::ApplicationManagerBase(int* argc, char*** argv, QObject* parent)
  43. : ApplicationManagerBase(argc, argv, parent, {})
  44. {
  45. }
  46. ApplicationManagerBase::ApplicationManagerBase(int* argc, char*** argv, AZ::ComponentApplicationSettings componentAppSettings)
  47. : ApplicationManagerBase(argc, argv, nullptr, AZStd::move(componentAppSettings))
  48. {
  49. }
  50. ApplicationManagerBase::ApplicationManagerBase(int* argc, char*** argv, QObject* parent, AZ::ComponentApplicationSettings componentAppSettings)
  51. : ApplicationManager(argc, argv, parent, AZStd::move(componentAppSettings))
  52. {
  53. qRegisterMetaType<AZ::u32>("AZ::u32");
  54. qRegisterMetaType<AZ::u32>("AZ::s64");
  55. qRegisterMetaType<AZ::Uuid>("AZ::Uuid");
  56. }
  57. ApplicationManagerBase::~ApplicationManagerBase()
  58. {
  59. AzToolsFramework::SourceControlNotificationBus::Handler::BusDisconnect();
  60. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  61. AssetProcessor::AssetBuilderRegistrationBus::Handler::BusDisconnect();
  62. AssetBuilderSDK::AssetBuilderBus::Handler::BusDisconnect();
  63. AssetProcessor::AssetBuilderInfoBus::Handler::BusDisconnect();
  64. if (m_settingsRegistryBuilder)
  65. {
  66. m_settingsRegistryBuilder->Uninitialize();
  67. }
  68. if (m_internalBuilder)
  69. {
  70. m_internalBuilder->UnInitialize();
  71. }
  72. for (AssetProcessor::ExternalModuleAssetBuilderInfo* externalAssetBuilderInfo : this->m_externalAssetBuilders)
  73. {
  74. externalAssetBuilderInfo->UnInitialize();
  75. delete externalAssetBuilderInfo;
  76. }
  77. Destroy();
  78. }
  79. AssetProcessor::RCController* ApplicationManagerBase::GetRCController() const
  80. {
  81. return m_rcController;
  82. }
  83. int ApplicationManagerBase::ProcessedAssetCount() const
  84. {
  85. return m_processedAssetCount;
  86. }
  87. int ApplicationManagerBase::FailedAssetsCount() const
  88. {
  89. return static_cast<int>(m_failedAssets.size());
  90. }
  91. void ApplicationManagerBase::ResetProcessedAssetCount()
  92. {
  93. m_processedAssetCount = 0;
  94. }
  95. void ApplicationManagerBase::ResetFailedAssetCount()
  96. {
  97. m_failedAssets = AZStd::set<AZStd::string>{};
  98. }
  99. AssetProcessor::AssetScanner* ApplicationManagerBase::GetAssetScanner() const
  100. {
  101. return m_assetScanner;
  102. }
  103. AssetProcessor::AssetProcessorManager* ApplicationManagerBase::GetAssetProcessorManager() const
  104. {
  105. return m_assetProcessorManager;
  106. }
  107. AssetProcessor::PlatformConfiguration* ApplicationManagerBase::GetPlatformConfiguration() const
  108. {
  109. return m_platformConfiguration;
  110. }
  111. ConnectionManager* ApplicationManagerBase::GetConnectionManager() const
  112. {
  113. return m_connectionManager;
  114. }
  115. ApplicationServer* ApplicationManagerBase::GetApplicationServer() const
  116. {
  117. return m_applicationServer;
  118. }
  119. void ApplicationManagerBase::InitAssetProcessorManager(AZStd::vector<ApplicationManagerBase::APCommandLineSwitch>& commandLineInfo)
  120. {
  121. AssetProcessor::ThreadController<AssetProcessor::AssetProcessorManager>* assetProcessorHelper = new AssetProcessor::ThreadController<AssetProcessor::AssetProcessorManager>();
  122. addRunningThread(assetProcessorHelper);
  123. m_assetProcessorManager = assetProcessorHelper->initialize([this, &assetProcessorHelper]()
  124. {
  125. return new AssetProcessor::AssetProcessorManager(m_platformConfiguration, assetProcessorHelper);
  126. });
  127. QObject::connect(this, &ApplicationManagerBase::OnBuildersRegistered, m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::OnBuildersRegistered, Qt::QueuedConnection);
  128. connect(this, &ApplicationManagerBase::SourceControlReady, [this]()
  129. {
  130. m_sourceControlReady = true;
  131. });
  132. const AzFramework::CommandLine* commandLine = nullptr;
  133. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  134. const APCommandLineSwitch Command_waitOnLaunch(commandLineInfo, "waitOnLaunch", "Briefly pauses Asset Processor during initializiation. Useful if you want to attach a debugger.");
  135. const APCommandLineSwitch Command_zeroAnalysisMode(commandLineInfo, "zeroAnalysisMode", "Enables using file modification time when examining source assets for processing.");
  136. const APCommandLineSwitch Command_enableQueryLogging(commandLineInfo, "enableQueryLogging", "Enables logging database queries.");
  137. const APCommandLineSwitch Command_dependencyScanPattern(commandLineInfo, "dependencyScanPattern", "Scans assets that match the given pattern for missing product dependencies.");
  138. const APCommandLineSwitch Command_dsp(commandLineInfo, "dsp", Command_dependencyScanPattern.m_helpText);
  139. const APCommandLineSwitch Command_fileDependencyScanPattern(commandLineInfo, "fileDependencyScanPattern", "Used with dependencyScanPattern to farther filter the scan.");
  140. const APCommandLineSwitch Command_fdsp(commandLineInfo, "fdsp", Command_fileDependencyScanPattern.m_helpText);
  141. const APCommandLineSwitch Command_additionalScanFolders(commandLineInfo, "additionalScanFolders", "Used with dependencyScanPattern to farther filter the scan.");
  142. const APCommandLineSwitch Command_dependencyScanMaxIteration(commandLineInfo, "dependencyScanMaxIteration", "Used to limit the number of recursive searches per line when running dependencyScanPattern.");
  143. const APCommandLineSwitch Command_warningLevel(commandLineInfo, "warningLevel", "Configure the error and warning reporting level for AssetProcessor. Pass in 1 for fatal errors, 2 for fatal errors and warnings.");
  144. const APCommandLineSwitch Command_acceptInput(commandLineInfo, "acceptInput", "Enable external control messaging via the ControlRequestHandler, used with automated tests.");
  145. const APCommandLineSwitch Command_debugOutput(commandLineInfo, "debugOutput", "When enabled, builders that support it will output debug information as product assets. Used primarily with scene files.");
  146. const APCommandLineSwitch Command_truncatefingerprint(commandLineInfo, "truncatefingerprint", "Truncates the fingerprint used for processed assets. Useful if you plan to compress product assets to share on another machine because some compression formats like zip will truncate file mod timestamps.");
  147. const APCommandLineSwitch Command_reprocessFileList(commandLineInfo, "reprocessFileList", "Reprocesses files in the passed in newline separated text file.");
  148. if (commandLine->HasSwitch(Command_waitOnLaunch.m_switch))
  149. {
  150. // Useful for attaching the debugger, this forces a short pause.
  151. AZStd::this_thread::sleep_for(AZStd::chrono::seconds(20));
  152. }
  153. if (commandLine->HasSwitch(Command_zeroAnalysisMode.m_switch))
  154. {
  155. m_assetProcessorManager->SetEnableModtimeSkippingFeature(true);
  156. }
  157. if (commandLine->HasSwitch(Command_enableQueryLogging.m_switch))
  158. {
  159. m_assetProcessorManager->SetQueryLogging(true);
  160. }
  161. if (commandLine->HasSwitch(Command_dependencyScanPattern.m_switch))
  162. {
  163. m_dependencyScanPattern = commandLine->GetSwitchValue(Command_dependencyScanPattern.m_switch, 0).c_str();
  164. }
  165. else if (commandLine->HasSwitch(Command_dsp.m_switch))
  166. {
  167. m_dependencyScanPattern = commandLine->GetSwitchValue(Command_dsp.m_switch, 0).c_str();
  168. }
  169. if (commandLine->HasSwitch(Command_reprocessFileList.m_switch))
  170. {
  171. m_reprocessFileList = commandLine->GetSwitchValue(Command_reprocessFileList.m_switch, 0).c_str();
  172. }
  173. m_fileDependencyScanPattern = "*";
  174. if (commandLine->HasSwitch(Command_fileDependencyScanPattern.m_switch))
  175. {
  176. m_fileDependencyScanPattern = commandLine->GetSwitchValue(Command_fileDependencyScanPattern.m_switch, 0).c_str();
  177. }
  178. else if (commandLine->HasSwitch(Command_fdsp.m_switch))
  179. {
  180. m_fileDependencyScanPattern = commandLine->GetSwitchValue(Command_fdsp.m_switch, 0).c_str();
  181. }
  182. if (commandLine->HasSwitch(Command_additionalScanFolders.m_switch))
  183. {
  184. for (size_t idx = 0; idx < commandLine->GetNumSwitchValues(Command_additionalScanFolders.m_switch); idx++)
  185. {
  186. AZStd::string value = commandLine->GetSwitchValue(Command_additionalScanFolders.m_switch, idx);
  187. m_dependencyAddtionalScanFolders.emplace_back(AZStd::move(value));
  188. }
  189. }
  190. if (commandLine->HasSwitch(Command_dependencyScanMaxIteration.m_switch))
  191. {
  192. AZStd::string maxIterationAsString = commandLine->GetSwitchValue(Command_dependencyScanMaxIteration.m_switch, 0);
  193. m_dependencyScanMaxIteration = AZStd::stoi(maxIterationAsString);
  194. }
  195. if (commandLine->HasSwitch(Command_warningLevel.m_switch))
  196. {
  197. using namespace AssetProcessor;
  198. const AZStd::string& levelString = commandLine->GetSwitchValue(Command_warningLevel.m_switch, 0);
  199. WarningLevel warningLevel = WarningLevel::Default;
  200. switch(AZStd::stoi(levelString))
  201. {
  202. case 1:
  203. warningLevel = WarningLevel::FatalErrors;
  204. break;
  205. case 2:
  206. warningLevel = WarningLevel::FatalErrorsAndWarnings;
  207. break;
  208. }
  209. AssetProcessor::JobDiagnosticRequestBus::Broadcast(&AssetProcessor::JobDiagnosticRequestBus::Events::SetWarningLevel, warningLevel);
  210. }
  211. if (commandLine->HasSwitch(Command_acceptInput.m_switch))
  212. {
  213. InitControlRequestHandler();
  214. }
  215. if (commandLine->HasSwitch(Command_debugOutput.m_switch))
  216. {
  217. m_assetProcessorManager->SetBuilderDebugFlag(true);
  218. }
  219. if (commandLine->HasSwitch(Command_truncatefingerprint.m_switch))
  220. {
  221. // Zip archive format uses 2 second precision truncated
  222. const int ArchivePrecision = 2000;
  223. int precision = ArchivePrecision;
  224. if (commandLine->GetNumSwitchValues(Command_truncatefingerprint.m_switch) > 0)
  225. {
  226. precision = AZStd::stoi(commandLine->GetSwitchValue(Command_truncatefingerprint.m_switch, 0));
  227. if(precision < 1)
  228. {
  229. precision = 1;
  230. }
  231. }
  232. AssetUtilities::SetTruncateFingerprintTimestamp(precision);
  233. }
  234. }
  235. void ApplicationManagerBase::HandleCommandLineHelp(AZStd::vector<ApplicationManagerBase::APCommandLineSwitch>& commandLineInfo)
  236. {
  237. const AzFramework::CommandLine* commandLine = nullptr;
  238. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  239. if (!commandLine)
  240. {
  241. AZ_TracePrintf(
  242. "AssetProcessor",
  243. "Asset Processor Command Line information not available, help cannot be printed. This is an application initialization problem "
  244. "and should be resolved in code.\n");
  245. return;
  246. }
  247. const APCommandLineSwitch Command_help(commandLineInfo, "help", "Displays this message.");
  248. const APCommandLineSwitch Command_h(commandLineInfo, "h", Command_help.m_helpText);
  249. // The regset command line flag is checked elsewhere, but handled here to make the help text complete.
  250. const APCommandLineSwitch Command_regset(commandLineInfo, "regset", "Set the given registry key to the given value.");
  251. if (commandLine->HasSwitch(Command_help.m_switch) || commandLine->HasSwitch(Command_h.m_switch))
  252. {
  253. // Other O3DE tools have a more full featured system for registering command flags
  254. // that includes help output, but right now the AssetProcessor just checks strings
  255. // via HasSwitch. This means this help output has to be updated manually.
  256. AZ_TracePrintf("AssetProcessor", "Asset Processor Command Line Flags:\n");
  257. for ([[maybe_unused]] const auto& command : commandLineInfo)
  258. {
  259. AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", command.m_switch, command.m_helpText);
  260. }
  261. }
  262. }
  263. void ApplicationManagerBase::Rescan()
  264. {
  265. m_assetProcessorManager->SetEnableModtimeSkippingFeature(false);
  266. GetAssetScanner()->StartScan();
  267. }
  268. void ApplicationManagerBase::FastScan()
  269. {
  270. m_assetProcessorManager->SetEnableModtimeSkippingFeature(true);
  271. GetAssetScanner()->StartScan();
  272. }
  273. void ApplicationManagerBase::InitAssetCatalog()
  274. {
  275. using namespace AssetProcessor;
  276. if (m_assetCatalog)
  277. {
  278. return;
  279. }
  280. ThreadController<AssetCatalog>* assetCatalogHelper = new ThreadController<AssetCatalog>();
  281. addRunningThread(assetCatalogHelper);
  282. m_assetCatalog = assetCatalogHelper->initialize([this, &assetCatalogHelper]()
  283. {
  284. AssetProcessor::AssetCatalog* catalog = new AssetCatalog(assetCatalogHelper, m_platformConfiguration);
  285. // Using a direct connection so we know the catalog has been updated before continuing on with code might depend on the asset being in the catalog
  286. connect(m_assetProcessorManager, &AssetProcessorManager::AssetMessage, catalog, &AssetCatalog::OnAssetMessage, Qt::DirectConnection);
  287. connect(m_assetProcessorManager, &AssetProcessorManager::SourceQueued, catalog, &AssetCatalog::OnSourceQueued);
  288. connect(m_assetProcessorManager, &AssetProcessorManager::SourceFinished, catalog, &AssetCatalog::OnSourceFinished);
  289. connect(m_assetProcessorManager, &AssetProcessorManager::PathDependencyResolved, catalog, &AssetCatalog::OnDependencyResolved);
  290. connect(
  291. catalog,
  292. &AssetCatalog::SendAssetMessage,
  293. this,
  294. [](auto message)
  295. {
  296. AssetProcessor::ConnectionBus::Broadcast(&AssetProcessor::ConnectionBus::Events::SendPerPlatform, 0, message, QString::fromUtf8(message.m_platform.c_str()));
  297. },
  298. Qt::QueuedConnection);
  299. connect(m_connectionManager, &ConnectionManager::ConnectionReady, catalog, &AssetCatalog::OnConnect, Qt::QueuedConnection);
  300. connect(catalog, &AssetCatalog::CatalogLoaded, m_assetProcessorManager, &AssetProcessorManager::OnCatalogReady);
  301. return catalog;
  302. });
  303. ConnectAssetCatalog();
  304. // schedule the asset catalog to build its registry in its own thread:
  305. QMetaObject::invokeMethod(m_assetCatalog, "BuildRegistry", Qt::QueuedConnection);
  306. }
  307. void ApplicationManagerBase::ConnectAssetCatalog()
  308. {
  309. using namespace AssetProcessor;
  310. auto router = AZ::Interface<IRequestRouter>::Get();
  311. if (router)
  312. {
  313. router->RegisterQueuedCallbackHandler(GetAssetCatalog(), &AssetCatalog::HandleSaveAssetCatalogRequest);
  314. router->RegisterQueuedCallbackHandler(GetAssetCatalog(), &AssetCatalog::HandleGetUnresolvedDependencyCountsRequest);
  315. }
  316. }
  317. void ApplicationManagerBase::InitRCController()
  318. {
  319. m_rcController = new AssetProcessor::RCController(m_platformConfiguration->GetMinJobs(), m_platformConfiguration->GetMaxJobs());
  320. QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetToProcess, m_rcController, &AssetProcessor::RCController::JobSubmitted);
  321. QObject::connect(m_rcController, &AssetProcessor::RCController::FileCompiled, m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetProcessed, Qt::UniqueConnection);
  322. QObject::connect(m_rcController, &AssetProcessor::RCController::FileFailed, m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetFailed);
  323. QObject::connect(m_rcController, &AssetProcessor::RCController::FileCancelled, m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetCancelled);
  324. QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::EscalateJobs, m_rcController, &AssetProcessor::RCController::EscalateJobs);
  325. QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::SourceDeleted, m_rcController, &AssetProcessor::RCController::RemoveJobsBySource);
  326. QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::JobComplete, m_rcController, &AssetProcessor::RCController::OnJobComplete);
  327. QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AddedToCatalog, m_rcController, &AssetProcessor::RCController::OnAddedToCatalog);
  328. }
  329. void ApplicationManagerBase::DestroyRCController()
  330. {
  331. if (m_rcController)
  332. {
  333. delete m_rcController;
  334. m_rcController = nullptr;
  335. }
  336. }
  337. void ApplicationManagerBase::InitAssetScanner()
  338. {
  339. using namespace AssetProcessor;
  340. m_assetScanner = new AssetScanner(m_platformConfiguration);
  341. // // wait until file cache is ready before attempting to build the catalog.
  342. QObject::connect(
  343. m_assetProcessorManager,
  344. &AssetProcessorManager::FileCacheIsReady,
  345. m_assetProcessorManager,
  346. [this]()
  347. {
  348. InitAssetCatalog();
  349. });
  350. QObject::connect(m_assetScanner, &AssetScanner::AssetScanningStatusChanged, m_assetProcessorManager, &AssetProcessorManager::OnAssetScannerStatusChange);
  351. QObject::connect(m_assetScanner, &AssetScanner::FilesFound, m_assetProcessorManager, &AssetProcessorManager::RecordFilesFromScanner);
  352. QObject::connect(m_assetScanner, &AssetScanner::FoldersFound, m_assetProcessorManager, &AssetProcessorManager::RecordFoldersFromScanner);
  353. QObject::connect(m_assetScanner, &AssetScanner::ExcludedFound, m_assetProcessorManager, &AssetProcessorManager::RecordExcludesFromScanner);
  354. QObject::connect(m_assetScanner, &AssetScanner::FilesFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
  355. QObject::connect(m_assetScanner, &AssetScanner::FoldersFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
  356. QObject::connect(m_assetScanner, &AssetScanner::ExcludedFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
  357. // file table
  358. QObject::connect(m_assetScanner, &AssetScanner::AssetScanningStatusChanged, m_fileProcessor.get(), &FileProcessor::OnAssetScannerStatusChange);
  359. QObject::connect(m_assetScanner, &AssetScanner::FilesFound, m_fileProcessor.get(), &FileProcessor::AssessFilesFromScanner);
  360. QObject::connect(m_assetScanner, &AssetScanner::FoldersFound, m_fileProcessor.get(), &FileProcessor::AssessFoldersFromScanner);
  361. }
  362. void ApplicationManagerBase::DestroyAssetScanner()
  363. {
  364. if (m_assetScanner)
  365. {
  366. delete m_assetScanner;
  367. m_assetScanner = nullptr;
  368. }
  369. }
  370. bool ApplicationManagerBase::InitPlatformConfiguration()
  371. {
  372. m_platformConfiguration = new AssetProcessor::PlatformConfiguration();
  373. QDir assetRoot;
  374. AssetUtilities::ComputeAssetRoot(assetRoot);
  375. return m_platformConfiguration->InitializeFromConfigFiles(GetSystemRoot().absolutePath(), assetRoot.absolutePath(), GetProjectPath());
  376. }
  377. bool ApplicationManagerBase::InitBuilderConfiguration()
  378. {
  379. m_builderConfig = AZStd::make_unique<AssetProcessor::BuilderConfigurationManager>();
  380. QString configFile = QDir(GetProjectPath()).absoluteFilePath(AssetProcessor::BuilderConfigFile);
  381. if (!QFile::exists(configFile))
  382. {
  383. AZ_TracePrintf("AssetProcessor", "No builder configuration file found at %s - skipping\n", configFile.toUtf8().data());
  384. return false;
  385. }
  386. if (!m_builderConfig->LoadConfiguration(configFile.toStdString().c_str()))
  387. {
  388. AZ_Error("AssetProcessor", false, "Failed to Initialize from %s - check the log files in the logs/ subfolder for more information.", configFile.toUtf8().data());
  389. return false;
  390. }
  391. return true;
  392. }
  393. void ApplicationManagerBase::DestroyPlatformConfiguration()
  394. {
  395. if (m_platformConfiguration)
  396. {
  397. delete m_platformConfiguration;
  398. m_platformConfiguration = nullptr;
  399. }
  400. }
  401. void ApplicationManagerBase::InitFileMonitor(AZStd::unique_ptr<FileWatcherBase> fileWatcher)
  402. {
  403. m_fileWatcher = AZStd::move(fileWatcher);
  404. using AssetProcessor::IntermediateAssetsFolderName;
  405. using AssetProcessor::AssetProcessorManager;
  406. using AssetProcessor::ScanFolderInfo;
  407. using AssetProcessor::ExcludedFolderCacheInterface;
  408. using AssetProcessor::FileProcessor;
  409. QString projectPath = GetProjectPath();
  410. QDir cacheRoot;
  411. AssetUtilities::ComputeProjectCacheRoot(cacheRoot);
  412. m_fileWatcher->InstallDefaultExclusionRules(cacheRoot.absolutePath(), projectPath);
  413. if (!cacheRoot.isEmpty())
  414. {
  415. // note that in projects, if we watch the project root, the cache folder is a subfolder of that folder anyway,
  416. // so AddFolderWatch below for the cache does nothing. In the case where the project might watch only a specific
  417. // subfolder, then this matters.
  418. m_fileWatcher->AddFolderWatch(cacheRoot.absolutePath(), true);
  419. }
  420. for (int folderIdx = 0; folderIdx < m_platformConfiguration->GetScanFolderCount(); ++folderIdx)
  421. {
  422. const ScanFolderInfo& info = m_platformConfiguration->GetScanFolderAt(folderIdx);
  423. m_fileWatcher->AddFolderWatch(info.ScanPath(), info.RecurseSubFolders());
  424. }
  425. const auto OnFileAdded = [this](QString path)
  426. {
  427. m_fileStateCache->AddFile(path);
  428. };
  429. const auto OnFileModified = [this](QString path)
  430. {
  431. m_fileStateCache->UpdateFile(path);
  432. m_uuidManager->FileChanged(path.toUtf8().constData());
  433. };
  434. const auto OnFileRemoved = [this](QString path)
  435. {
  436. m_fileStateCache->RemoveFile(path);
  437. m_uuidManager->FileRemoved(path.toUtf8().constData());
  438. };
  439. connect(m_fileWatcher.get(), &FileWatcher::fileAdded, OnFileAdded);
  440. connect(m_fileWatcher.get(), &FileWatcher::fileModified, OnFileModified);
  441. connect(m_fileWatcher.get(), &FileWatcher::fileRemoved, OnFileRemoved);
  442. auto excludedFolderCacheInterfacePtr = AZ::Interface<ExcludedFolderCacheInterface>::Get();
  443. if (!excludedFolderCacheInterfacePtr)
  444. {
  445. AZ_Error("AssetProcessor", false, "ExcludedFolderCacheInterface not found.");
  446. }
  447. else
  448. {
  449. const auto OnFileAddedForExcludeFolderCache = [excludedFolderCacheInterfacePtr](QString path)
  450. {
  451. excludedFolderCacheInterfacePtr->FileAdded(path);
  452. };
  453. connect(m_fileWatcher.get(), &FileWatcher::fileAdded, OnFileAddedForExcludeFolderCache);
  454. }
  455. if (m_fileProcessor.get())
  456. {
  457. connect(m_fileWatcher.get(), &FileWatcher::fileAdded, m_fileProcessor.get(), &FileProcessor::AssessAddedFile, Qt::QueuedConnection);
  458. connect(m_fileWatcher.get(), &FileWatcher::fileRemoved, m_fileProcessor.get(), &FileProcessor::AssessDeletedFile, Qt::QueuedConnection);
  459. }
  460. connect(m_fileWatcher.get(), &FileWatcher::fileAdded, m_assetProcessorManager, &AssetProcessorManager::AssessAddedFile, Qt::QueuedConnection);
  461. connect(m_fileWatcher.get(), &FileWatcher::fileModified, m_assetProcessorManager, &AssetProcessorManager::AssessModifiedFile, Qt::QueuedConnection);
  462. connect(m_fileWatcher.get(), &FileWatcher::fileRemoved, m_assetProcessorManager, &AssetProcessorManager::AssessDeletedFile, Qt::QueuedConnection);
  463. }
  464. void ApplicationManagerBase::DestroyFileMonitor()
  465. {
  466. if(m_fileWatcher)
  467. {
  468. m_fileWatcher->ClearFolderWatches();
  469. m_fileWatcher = nullptr;
  470. }
  471. }
  472. void ApplicationManagerBase::DestroyApplicationServer()
  473. {
  474. if (m_applicationServer)
  475. {
  476. delete m_applicationServer;
  477. m_applicationServer = nullptr;
  478. }
  479. }
  480. void ApplicationManagerBase::DestroyControlRequestHandler()
  481. {
  482. if (m_controlRequestHandler)
  483. {
  484. delete m_controlRequestHandler;
  485. m_controlRequestHandler = nullptr;
  486. }
  487. }
  488. void ApplicationManagerBase::InitControlRequestHandler()
  489. {
  490. m_controlRequestHandler = new ControlRequestHandler(this);
  491. }
  492. void ApplicationManagerBase::InitConnectionManager()
  493. {
  494. using namespace AzFramework::AssetSystem;
  495. using namespace AzToolsFramework::AssetSystem;
  496. m_connectionManager = new ConnectionManager();
  497. //Application manager related stuff
  498. // The AssetCatalog has to be rebuilt on connection, so we force the incoming connection messages to be serialized as they connect to the ApplicationManagerBase
  499. [[maybe_unused]] bool result = QObject::connect(m_applicationServer, &ApplicationServer::newIncomingConnection, m_connectionManager, &ConnectionManager::NewConnection, Qt::QueuedConnection);
  500. AZ_Assert(result, "Failed to connect to ApplicationServer signal");
  501. //RcController related stuff
  502. result = QObject::connect(GetRCController(), &AssetProcessor::RCController::JobStatusChanged, GetAssetProcessorManager(), &AssetProcessor::AssetProcessorManager::OnJobStatusChanged);
  503. AZ_Assert(result, "Failed to connect to RCController signal");
  504. result = QObject::connect(GetRCController(), &AssetProcessor::RCController::JobStarted, this,
  505. [](QString inputFile, QString platform)
  506. {
  507. QString msg = QCoreApplication::translate("O3DE Asset Processor", "Processing %1 (%2)...\n", "%1 is the name of the file, and %2 is the platform to process it for").arg(inputFile, platform);
  508. AZ_Printf(AssetProcessor::ConsoleChannel, "%s", msg.toUtf8().constData());
  509. AssetNotificationMessage message(inputFile.toUtf8().constData(), AssetNotificationMessage::JobStarted, AZ::Data::s_invalidAssetType, platform.toUtf8().constData());
  510. AssetProcessor::ConnectionBus::Broadcast(&AssetProcessor::ConnectionBus::Events::SendPerPlatform, 0, message, platform);
  511. }
  512. );
  513. AZ_Assert(result, "Failed to connect to RCController signal");
  514. result = QObject::connect(
  515. GetRCController(),
  516. &AssetProcessor::RCController::FileCompiled,
  517. this,
  518. [](AssetProcessor::JobEntry entry, AssetBuilderSDK::ProcessJobResponse /*response*/)
  519. {
  520. AssetNotificationMessage message(
  521. entry.m_sourceAssetReference.RelativePath().c_str(),
  522. AssetNotificationMessage::JobCompleted,
  523. AZ::Data::s_invalidAssetType,
  524. entry.m_platformInfo.m_identifier.c_str());
  525. AssetProcessor::ConnectionBus::Broadcast(
  526. &AssetProcessor::ConnectionBus::Events::SendPerPlatform,
  527. 0,
  528. message,
  529. QString::fromUtf8(entry.m_platformInfo.m_identifier.c_str()));
  530. });
  531. AZ_Assert(result, "Failed to connect to RCController signal");
  532. result = QObject::connect(
  533. GetRCController(),
  534. &AssetProcessor::RCController::FileFailed,
  535. this,
  536. [](AssetProcessor::JobEntry entry)
  537. {
  538. AssetNotificationMessage message(
  539. entry.m_sourceAssetReference.RelativePath().c_str(),
  540. AssetNotificationMessage::JobFailed,
  541. AZ::Data::s_invalidAssetType,
  542. entry.m_platformInfo.m_identifier.c_str());
  543. AssetProcessor::ConnectionBus::Broadcast(
  544. &AssetProcessor::ConnectionBus::Events::SendPerPlatform,
  545. 0,
  546. message,
  547. QString::fromUtf8(entry.m_platformInfo.m_identifier.c_str()));
  548. });
  549. AZ_Assert(result, "Failed to connect to RCController signal");
  550. result = QObject::connect(GetRCController(), &AssetProcessor::RCController::JobsInQueuePerPlatform, this,
  551. [](QString platform, int count)
  552. {
  553. AssetNotificationMessage message(QByteArray::number(count).constData(), AssetNotificationMessage::JobCount, AZ::Data::s_invalidAssetType, platform.toUtf8().constData());
  554. AssetProcessor::ConnectionBus::Broadcast(&AssetProcessor::ConnectionBus::Events::SendPerPlatform, 0, message, platform);
  555. }
  556. );
  557. AZ_Assert(result, "Failed to connect to RCController signal");
  558. m_connectionManager->RegisterService(RequestPing::MessageType,
  559. AZStd::bind([](unsigned int connId, unsigned int /*type*/, unsigned int serial, QByteArray /*payload*/)
  560. {
  561. ResponsePing responsePing;
  562. AssetProcessor::ConnectionBus::Event(connId, &AssetProcessor::ConnectionBus::Events::SendResponse, serial, responsePing);
  563. }, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4)
  564. );
  565. m_connectionManager->RegisterService(
  566. AssetBuilder::BuilderRegistrationRequest::MessageType,
  567. [this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload, QString)
  568. {
  569. AssetBuilder::BuilderRegistrationRequest registrationRequest;
  570. if (m_builderRegistrationComplete)
  571. {
  572. return;
  573. }
  574. m_builderRegistrationComplete = true;
  575. if (AssetProcessor::UnpackMessage(payload, registrationRequest))
  576. {
  577. for (const auto& builder : registrationRequest.m_builders)
  578. {
  579. AssetBuilderSDK::AssetBuilderDesc desc;
  580. desc.m_name = builder.m_name;
  581. desc.m_patterns = builder.m_patterns;
  582. desc.m_version = builder.m_version;
  583. desc.m_analysisFingerprint = builder.m_analysisFingerprint;
  584. desc.m_flags = builder.m_flags;
  585. desc.m_busId = builder.m_busId;
  586. desc.m_flagsByJobKey = builder.m_flagsByJobKey;
  587. desc.m_productsToKeepOnFailure = builder.m_productsToKeepOnFailure;
  588. // Builders registered this way are always external builders
  589. desc.m_builderType = AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::External;
  590. RegisterBuilderInformation(desc);
  591. }
  592. QTimer::singleShot(
  593. 0, this,
  594. [this]()
  595. {
  596. if (!PostActivate())
  597. {
  598. QuitRequested();
  599. }
  600. });
  601. }
  602. });
  603. //You can get Asset Processor Current State
  604. using AzFramework::AssetSystem::RequestAssetProcessorStatus;
  605. auto GetState = [this](unsigned int connId, unsigned int, unsigned int serial, QByteArray payload, QString)
  606. {
  607. RequestAssetProcessorStatus requestAssetProcessorMessage;
  608. if (AssetProcessor::UnpackMessage(payload, requestAssetProcessorMessage))
  609. {
  610. bool status = false;
  611. //check whether the scan is complete,the asset processor manager initial processing is complete and
  612. //the number of copy jobs are zero
  613. int numberOfPendingJobs = GetRCController()->NumberOfPendingCriticalJobsPerPlatform(requestAssetProcessorMessage.m_platform.c_str());
  614. status = (GetAssetScanner()->status() == AssetProcessor::AssetScanningStatus::Completed)
  615. && m_assetProcessorManagerIsReady
  616. && (!numberOfPendingJobs);
  617. ResponseAssetProcessorStatus responseAssetProcessorMessage;
  618. responseAssetProcessorMessage.m_isAssetProcessorReady = status;
  619. responseAssetProcessorMessage.m_numberOfPendingJobs = numberOfPendingJobs + m_remainingAPMJobs;
  620. if (responseAssetProcessorMessage.m_numberOfPendingJobs && m_highestConnId < connId)
  621. {
  622. // We will just emit this status message once per connId
  623. Q_EMIT ConnectionStatusMsg(QString(" Critical assets need to be processed for %1 platform. Editor/Game will launch once they are processed.").arg(requestAssetProcessorMessage.m_platform.c_str()));
  624. m_highestConnId = connId;
  625. }
  626. AssetProcessor::ConnectionBus::Event(
  627. connId, &AssetProcessor::ConnectionBus::Events::SendResponse, serial, responseAssetProcessorMessage);
  628. }
  629. };
  630. // connect the network messages to the Request handler:
  631. m_connectionManager->RegisterService(RequestAssetProcessorStatus::MessageType, GetState);
  632. // ability to see if an asset platform is enabled or not
  633. using AzToolsFramework::AssetSystem::AssetProcessorPlatformStatusRequest;
  634. m_connectionManager->RegisterService(AssetProcessorPlatformStatusRequest::MessageType,
  635. [](unsigned int connId, unsigned int, unsigned int serial, QByteArray payload, QString)
  636. {
  637. AssetProcessorPlatformStatusResponse responseMessage;
  638. AssetProcessorPlatformStatusRequest requestMessage;
  639. if (AssetProcessor::UnpackMessage(payload, requestMessage))
  640. {
  641. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(responseMessage.m_isPlatformEnabled,
  642. &AzToolsFramework::AssetSystemRequestBus::Events::IsAssetPlatformEnabled, requestMessage.m_platform.c_str());
  643. }
  644. AssetProcessor::ConnectionBus::Event(connId,
  645. &AssetProcessor::ConnectionBus::Events::SendResponse, serial, responseMessage);
  646. });
  647. // check the total number of assets remaining for a specified platform
  648. using AzToolsFramework::AssetSystem::AssetProcessorPendingPlatformAssetsRequest;
  649. m_connectionManager->RegisterService(AssetProcessorPendingPlatformAssetsRequest::MessageType,
  650. [this](unsigned int connId, unsigned int, unsigned int serial, QByteArray payload, QString)
  651. {
  652. AssetProcessorPendingPlatformAssetsResponse responseMessage;
  653. AssetProcessorPendingPlatformAssetsRequest requestMessage;
  654. if (AssetProcessor::UnpackMessage(payload, requestMessage))
  655. {
  656. const char* platformIdentifier = requestMessage.m_platform.c_str();
  657. responseMessage.m_numberOfPendingJobs =
  658. GetRCController()->NumberOfPendingJobsPerPlatform(platformIdentifier);
  659. }
  660. AssetProcessor::ConnectionBus::Event(connId,
  661. &AssetProcessor::ConnectionBus::Events::SendResponse, serial, responseMessage);
  662. });
  663. }
  664. void ApplicationManagerBase::DestroyConnectionManager()
  665. {
  666. if (m_connectionManager)
  667. {
  668. delete m_connectionManager;
  669. m_connectionManager = nullptr;
  670. }
  671. }
  672. void ApplicationManagerBase::InitAssetRequestHandler(AssetProcessor::AssetRequestHandler* assetRequestHandler)
  673. {
  674. using namespace AzFramework::AssetSystem;
  675. using namespace AzToolsFramework::AssetSystem;
  676. using namespace AzFramework::AssetSystem;
  677. using namespace AssetProcessor;
  678. m_assetRequestHandler = assetRequestHandler;
  679. auto router = AZ::Interface<IRequestRouter>::Get();
  680. if (router)
  681. {
  682. router->RegisterQueuedCallbackHandler(GetAssetProcessorManager(), &AssetProcessorManager::ProcessFingerprintClearRequest);
  683. router->RegisterQueuedCallbackHandler(GetAssetProcessorManager(), &AssetProcessorManager::ProcessGetAssetJobsInfoRequest);
  684. router->RegisterQueuedCallbackHandler(GetAssetProcessorManager(), &AssetProcessorManager::ProcessGetAssetJobLogRequest);
  685. router->RegisterQueuedCallbackHandler(GetAssetProcessorManager(), &AssetProcessorManager::ProcessGetAbsoluteAssetDatabaseLocationRequest);
  686. }
  687. // connect the "Does asset exist?" loop to each other:
  688. QObject::connect(m_assetRequestHandler, &AssetRequestHandler::RequestAssetExists, GetAssetProcessorManager(), &AssetProcessorManager::OnRequestAssetExists);
  689. QObject::connect(GetAssetProcessorManager(), &AssetProcessorManager::SendAssetExistsResponse, m_assetRequestHandler, &AssetRequestHandler::OnRequestAssetExistsResponse);
  690. QObject::connect(GetAssetProcessorManager(), &AssetProcessorManager::FenceFileDetected, m_assetRequestHandler, &AssetRequestHandler::OnFenceFileDetected);
  691. // connect the Asset Request Handler to RC:
  692. QObject::connect(m_assetRequestHandler, &AssetRequestHandler::RequestCompileGroup, GetRCController(), &RCController::OnRequestCompileGroup);
  693. QObject::connect(m_assetRequestHandler, &AssetRequestHandler::RequestEscalateAssetBySearchTerm, GetRCController(), &RCController::OnEscalateJobsBySearchTerm);
  694. QObject::connect(m_assetRequestHandler, &AssetRequestHandler::RequestEscalateAssetByUuid, GetRCController(), &RCController::OnEscalateJobsBySourceUUID);
  695. QObject::connect(GetRCController(), &RCController::CompileGroupCreated, m_assetRequestHandler, &AssetRequestHandler::OnCompileGroupCreated);
  696. QObject::connect(GetRCController(), &RCController::CompileGroupFinished, m_assetRequestHandler, &AssetRequestHandler::OnCompileGroupFinished);
  697. QObject::connect(GetAssetProcessorManager(), &AssetProcessor::AssetProcessorManager::NumRemainingJobsChanged, this, [this](int newNum, QString extraInfo)
  698. {
  699. if (!m_assetProcessorManagerIsReady)
  700. {
  701. if ((m_remainingAPMJobs == newNum) && m_remainingAPMJobs)
  702. {
  703. return;
  704. }
  705. m_remainingAPMJobs = newNum;
  706. if (!m_remainingAPMJobs)
  707. {
  708. m_assetProcessorManagerIsReady = true;
  709. }
  710. }
  711. AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Analyzing_Jobs, newNum, extraInfo);
  712. Q_EMIT AssetProcessorStatusChanged(entry);
  713. });
  714. }
  715. void ApplicationManagerBase::InitFileStateCache()
  716. {
  717. const AzFramework::CommandLine* commandLine = nullptr;
  718. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  719. if (commandLine->HasSwitch("disableFileCache"))
  720. {
  721. m_fileStateCache = AZStd::make_unique<AssetProcessor::FileStatePassthrough>();
  722. return;
  723. }
  724. m_fileStateCache = AZStd::make_unique<AssetProcessor::FileStateCache>();
  725. }
  726. void ApplicationManagerBase::InitUuidManager()
  727. {
  728. m_uuidManager = AZStd::make_unique<AssetProcessor::UuidManager>();
  729. AssetProcessor::UuidSettings uuidSettings;
  730. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  731. if (settingsRegistry)
  732. {
  733. if (settingsRegistry->GetObject(uuidSettings, "/O3DE/AssetProcessor/Settings/Metadata"))
  734. {
  735. m_uuidManager->EnableGenerationForTypes(uuidSettings.m_enabledTypes);
  736. m_assetProcessorManager->SetMetaCreationDelay(uuidSettings.m_metaCreationDelayMs);
  737. }
  738. }
  739. }
  740. ApplicationManager::BeforeRunStatus ApplicationManagerBase::BeforeRun()
  741. {
  742. ApplicationManager::BeforeRunStatus status = ApplicationManager::BeforeRun();
  743. if (status != ApplicationManager::BeforeRunStatus::Status_Success)
  744. {
  745. return status;
  746. }
  747. //Register all QMetatypes here
  748. qRegisterMetaType<AzFramework::AssetSystem::AssetStatus>("AzFramework::AssetSystem::AssetStatus");
  749. qRegisterMetaType<AzFramework::AssetSystem::AssetStatus>("AssetStatus");
  750. qRegisterMetaType<AssetProcessor::AssetScanningStatus>("AssetScanningStatus");
  751. qRegisterMetaType<AssetProcessor::NetworkRequestID>("NetworkRequestID");
  752. qRegisterMetaType<AssetProcessor::JobEntry>("JobEntry");
  753. qRegisterMetaType<AzToolsFramework::AssetSystem::JobInfo>("AzToolsFramework::AssetSystem::JobInfo");
  754. qRegisterMetaType<AssetBuilderSDK::ProcessJobResponse>("ProcessJobResponse");
  755. qRegisterMetaType<AzToolsFramework::AssetSystem::JobStatus>("AzToolsFramework::AssetSystem::JobStatus");
  756. qRegisterMetaType<AzToolsFramework::AssetSystem::JobStatus>("JobStatus");
  757. qRegisterMetaType<AssetProcessor::JobDetails>("JobDetails");
  758. qRegisterMetaType<AZ::Data::AssetId>("AZ::Data::AssetId");
  759. qRegisterMetaType<AZ::Data::AssetInfo>("AZ::Data::AssetInfo");
  760. qRegisterMetaType<AzToolsFramework::AssetSystem::AssetJobLogRequest>("AzToolsFramework::AssetSystem::AssetJobLogRequest");
  761. qRegisterMetaType<AzToolsFramework::AssetSystem::AssetJobLogRequest>("AssetJobLogRequest");
  762. qRegisterMetaType<AzToolsFramework::AssetSystem::AssetJobLogResponse>("AzToolsFramework::AssetSystem::AssetJobLogResponse");
  763. qRegisterMetaType<AzToolsFramework::AssetSystem::AssetJobLogResponse>("AssetJobLogResponse");
  764. qRegisterMetaType<AzFramework::AssetSystem::BaseAssetProcessorMessage*>("AzFramework::AssetSystem::BaseAssetProcessorMessage*");
  765. qRegisterMetaType<AzFramework::AssetSystem::BaseAssetProcessorMessage*>("BaseAssetProcessorMessage*");
  766. qRegisterMetaType<AssetProcessor::JobIdEscalationList>("AssetProcessor::JobIdEscalationList");
  767. qRegisterMetaType<AzFramework::AssetSystem::AssetNotificationMessage>("AzFramework::AssetSystem::AssetNotificationMessage");
  768. qRegisterMetaType<AzFramework::AssetSystem::AssetNotificationMessage>("AssetNotificationMessage");
  769. qRegisterMetaType<AZStd::string>("AZStd::string");
  770. qRegisterMetaType<AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry>("AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry");
  771. qRegisterMetaType<AssetProcessor::AssetCatalogStatus>("AssetCatalogStatus");
  772. qRegisterMetaType<AssetProcessor::AssetCatalogStatus>("AssetProcessor::AssetCatalogStatus");
  773. qRegisterMetaType<QSet<QString> >("QSet<QString>");
  774. qRegisterMetaType<QSet<AssetProcessor::AssetFileInfo>>("QSet<AssetFileInfo>");
  775. qRegisterMetaType<AssetProcessor::SourceAssetReference>("SourceAssetReference");
  776. qRegisterMetaType<AZStd::unordered_set<AZ::Uuid>>("AZStd::unordered_set<AZ::Uuid>");
  777. AssetBuilderSDK::AssetBuilderBus::Handler::BusConnect();
  778. AssetProcessor::AssetBuilderRegistrationBus::Handler::BusConnect();
  779. AssetProcessor::AssetBuilderInfoBus::Handler::BusConnect();
  780. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  781. AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
  782. return ApplicationManager::BeforeRunStatus::Status_Success;
  783. }
  784. void ApplicationManagerBase::Destroy()
  785. {
  786. delete m_ticker;
  787. m_ticker = nullptr;
  788. delete m_assetRequestHandler;
  789. m_assetRequestHandler = nullptr;
  790. // Destroy file monitor early so that no callbacks fire during shutdown.
  791. DestroyFileMonitor();
  792. ShutdownBuilderManager();
  793. ShutDownFileProcessor();
  794. DestroyControlRequestHandler();
  795. DestroyConnectionManager();
  796. DestroyAssetServerHandler();
  797. DestroyRCController();
  798. DestroyAssetScanner();
  799. ShutDownAssetDatabase();
  800. DestroyPlatformConfiguration();
  801. DestroyApplicationServer();
  802. }
  803. bool ApplicationManagerBase::Run()
  804. {
  805. bool showErrorMessageOnRegistryProblem = false;
  806. RegistryCheckInstructions registryCheckInstructions = CheckForRegistryProblems(nullptr, showErrorMessageOnRegistryProblem);
  807. if (registryCheckInstructions != RegistryCheckInstructions::Continue)
  808. {
  809. return false;
  810. }
  811. if (!Activate())
  812. {
  813. return false;
  814. }
  815. AZ_Printf(AssetProcessor::ConsoleChannel, "Asset Processor Batch Processing Started.\n");
  816. AZ_Printf(AssetProcessor::ConsoleChannel, "-----------------------------------------\n");
  817. QElapsedTimer allAssetsProcessingTimer;
  818. allAssetsProcessingTimer.start();
  819. m_duringStartup = false;
  820. qApp->exec();
  821. AZ_Printf(AssetProcessor::ConsoleChannel, "-----------------------------------------\n");
  822. AZ_Printf(AssetProcessor::ConsoleChannel, "Asset Processor Batch Processing complete\n");
  823. if (!m_failedAssets.empty())
  824. {
  825. AZ_Printf(AssetProcessor::ConsoleChannel, "---------------FAILED ASSETS-------------\n");
  826. for (const auto& failedAsset : m_failedAssets)
  827. {
  828. AZ_Printf(AssetProcessor::ConsoleChannel, "%s\n", failedAsset.c_str());
  829. }
  830. AZ_Printf(AssetProcessor::ConsoleChannel, "-----------------------------------------\n");
  831. }
  832. AZ_Printf(AssetProcessor::ConsoleChannel, "Number of Assets Successfully Processed: %d.\n", ProcessedAssetCount());
  833. AZ_Printf(AssetProcessor::ConsoleChannel, "Number of Assets Failed to Process: %d.\n", FailedAssetsCount());
  834. AZ_Printf(AssetProcessor::ConsoleChannel, "Number of Warnings Reported: %d.\n", m_warningCount);
  835. AZ_Printf(AssetProcessor::ConsoleChannel, "Number of Errors Reported: %d.\n", m_errorCount);
  836. AZ_Printf(AssetProcessor::ConsoleChannel, "Total Assets Processing Time: %fs\n", allAssetsProcessingTimer.elapsed() / 1000.0f);
  837. AZ_Printf(AssetProcessor::ConsoleChannel, "Asset Processor Batch Processing Completed.\n");
  838. RemoveOldTempFolders();
  839. Destroy();
  840. return FailedAssetsCount() == 0;
  841. }
  842. void ApplicationManagerBase::HandleFileRelocation() const
  843. {
  844. static constexpr char Delimiter[] = "--------------------------- RELOCATION REPORT ---------------------------\n";
  845. static constexpr char MoveCommand[] = "move";
  846. static constexpr char DeleteCommand[] = "delete";
  847. static constexpr char ConfirmCommand[] = "confirm";
  848. static constexpr char LeaveEmptyFoldersCommand[] = "leaveEmptyFolders";
  849. static constexpr char AllowBrokenDependenciesCommand[] = "allowBrokenDependencies";
  850. static constexpr char UpdateReferencesCommand[] = "updateReferences";
  851. static constexpr char ExcludeMetaDataFiles[] = "excludeMetaDataFiles";
  852. const AzFramework::CommandLine* commandLine = nullptr;
  853. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  854. const bool allowBrokenDependencies = commandLine->HasSwitch(AllowBrokenDependenciesCommand);
  855. const bool previewOnly = !commandLine->HasSwitch(ConfirmCommand);
  856. const bool leaveEmptyFolders = commandLine->HasSwitch(LeaveEmptyFoldersCommand);
  857. const bool doMove = commandLine->HasSwitch(MoveCommand);
  858. const bool doDelete = commandLine->HasSwitch(DeleteCommand);
  859. const bool updateReferences = commandLine->HasSwitch(UpdateReferencesCommand);
  860. const bool excludeMetaDataFiles = commandLine->HasSwitch(ExcludeMetaDataFiles);
  861. const int flags = (allowBrokenDependencies ? AssetProcessor::RelocationParameters_AllowDependencyBreakingFlag : 0) |
  862. (previewOnly ? AssetProcessor::RelocationParameters_PreviewOnlyFlag : 0) |
  863. (leaveEmptyFolders ? 0 : AssetProcessor::RelocationParameters_RemoveEmptyFoldersFlag) |
  864. (updateReferences ? AssetProcessor::RelocationParameters_UpdateReferencesFlag : 0) |
  865. (excludeMetaDataFiles ? AssetProcessor::RelocationParameters_ExcludeMetaDataFilesFlag : 0);
  866. if(doMove || doDelete)
  867. {
  868. int printCounter = 0;
  869. while(!m_sourceControlReady)
  870. {
  871. // We need to wait for source control to be ready before continuing
  872. if (printCounter % 10 == 0)
  873. {
  874. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Waiting for Source Control connection\n");
  875. }
  876. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
  877. AZ::TickBus::ExecuteQueuedEvents();
  878. ++printCounter;
  879. }
  880. }
  881. if(!doMove && updateReferences)
  882. {
  883. AZ_Error(AssetProcessor::ConsoleChannel, false, "Command --%s must be used with command --%s", UpdateReferencesCommand, MoveCommand);
  884. return;
  885. }
  886. // Print some errors to inform users that the move or delete command must be included
  887. if(!doMove && !doDelete)
  888. {
  889. AZ_Error(AssetProcessor::ConsoleChannel, previewOnly, "Command --%s must be used with command --%s or --%s", ConfirmCommand, MoveCommand, DeleteCommand);
  890. AZ_Error(AssetProcessor::ConsoleChannel, !leaveEmptyFolders, "Command --%s must be used with command --%s or --%s", LeaveEmptyFoldersCommand, MoveCommand, DeleteCommand);
  891. AZ_Error(AssetProcessor::ConsoleChannel, !allowBrokenDependencies, "Command --%s must be used with command --%s or --%s", AllowBrokenDependenciesCommand, MoveCommand, DeleteCommand);
  892. return;
  893. }
  894. if (doMove)
  895. {
  896. if (commandLine->GetNumSwitchValues(MoveCommand) != 2)
  897. {
  898. AZ_Error(AssetProcessor::ConsoleChannel, false, "Invalid format for move command. Expected format is %s=<source>,<destination>", MoveCommand);
  899. return;
  900. }
  901. AZ_Printf(AssetProcessor::ConsoleChannel, Delimiter);
  902. auto source = commandLine->GetSwitchValue(MoveCommand, 0);
  903. auto destination = commandLine->GetSwitchValue(MoveCommand, 1);
  904. AZ_Printf(AssetProcessor::ConsoleChannel, "Move Source: %s, Destination: %s\n", source.c_str(), destination.c_str());
  905. if(!previewOnly)
  906. {
  907. AZ_Printf(AssetProcessor::ConsoleChannel, "Performing real file move\n");
  908. if (leaveEmptyFolders)
  909. {
  910. AZ_Printf(AssetProcessor::ConsoleChannel, "Leaving empty folders\n");
  911. }
  912. else
  913. {
  914. AZ_Printf(AssetProcessor::ConsoleChannel, "Deleting empty folders\n");
  915. }
  916. if(updateReferences)
  917. {
  918. AZ_Printf(AssetProcessor::ConsoleChannel, "Attempting to perform reference fix-up\n");
  919. }
  920. }
  921. else
  922. {
  923. AZ_Printf(AssetProcessor::ConsoleChannel, "SETTING: Preview file move. Run again with --%s to actually make changes\n", ConfirmCommand);
  924. }
  925. auto* relocationInterface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
  926. if(relocationInterface)
  927. {
  928. auto result = relocationInterface->Move(source, destination, flags);
  929. if (result.IsSuccess())
  930. {
  931. AssetProcessor::RelocationSuccess success = result.TakeValue();
  932. // The report can be too long for the AZ_Printf buffer, so split it into individual lines
  933. AZStd::string report = relocationInterface->BuildReport(success.m_relocationContainer, success.m_updateTasks, true, updateReferences);
  934. AZStd::vector<AZStd::string> lines;
  935. AzFramework::StringFunc::Tokenize(report.c_str(), lines, "\n");
  936. for (const AZStd::string& line : lines)
  937. {
  938. AZ_Printf(AssetProcessor::ConsoleChannel, (line + "\n").c_str());
  939. }
  940. if (!previewOnly)
  941. {
  942. AZ_Printf(AssetProcessor::ConsoleChannel, "MOVE COMPLETE\n");
  943. AZ_Printf(AssetProcessor::ConsoleChannel, "TOTAL DEPENDENCIES FOUND: %d\n", success.m_updateTotalCount);
  944. AZ_Printf(AssetProcessor::ConsoleChannel, "SUCCESSFULLY UPDATED: %d\n", success.m_updateSuccessCount);
  945. AZ_Printf(AssetProcessor::ConsoleChannel, "FAILED TO UPDATE: %d\n", success.m_updateFailureCount);
  946. AZ_Printf(AssetProcessor::ConsoleChannel, "TOTAL FILES: %d\n", success.m_moveTotalCount);
  947. AZ_Printf(AssetProcessor::ConsoleChannel, "SUCCESS COUNT: %d\n", success.m_moveSuccessCount);
  948. AZ_Printf(AssetProcessor::ConsoleChannel, "FAILURE COUNT: %d\n", success.m_moveFailureCount);
  949. }
  950. }
  951. else
  952. {
  953. AssetProcessor::MoveFailure failure = result.TakeError();
  954. AZ_Printf(AssetProcessor::ConsoleChannel, failure.m_reason.c_str());
  955. if(failure.m_dependencyFailure)
  956. {
  957. AZ_Printf(AssetProcessor::ConsoleChannel, "To ignore and continue anyway, re-run this command with the --%s option OR re-run this command with the --%s option to attempt to fix-up references\n", AllowBrokenDependenciesCommand, UpdateReferencesCommand);
  958. }
  959. }
  960. }
  961. else
  962. {
  963. AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to retrieve ISourceFileRelocation interface");
  964. return;
  965. }
  966. AZ_Printf(AssetProcessor::ConsoleChannel, Delimiter);
  967. }
  968. else if(doDelete)
  969. {
  970. if(commandLine->GetNumSwitchValues(DeleteCommand) != 1)
  971. {
  972. AZ_Error(AssetProcessor::ConsoleChannel, false, "Invalid format for delete command. Expected format is %s=<source>", DeleteCommand);
  973. return;
  974. }
  975. AZ_Printf(AssetProcessor::ConsoleChannel, Delimiter);
  976. auto source = commandLine->GetSwitchValue(DeleteCommand, 0);
  977. AZ_Printf(AssetProcessor::ConsoleChannel, "Delete Source: %s\n", source.c_str());
  978. if (!previewOnly)
  979. {
  980. AZ_Printf(AssetProcessor::ConsoleChannel, "Performing real file delete\n");
  981. if (leaveEmptyFolders)
  982. {
  983. AZ_Printf(AssetProcessor::ConsoleChannel, "Leaving empty folders\n");
  984. }
  985. else
  986. {
  987. AZ_Printf(AssetProcessor::ConsoleChannel, "Deleting empty folders\n");
  988. }
  989. }
  990. else
  991. {
  992. AZ_Printf(AssetProcessor::ConsoleChannel, "SETTING: Preview file delete. Run again with --%s to actually make changes\n", ConfirmCommand);
  993. }
  994. auto* relocationInterface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
  995. if (relocationInterface)
  996. {
  997. auto result = relocationInterface->Delete(source, flags);
  998. if (result.IsSuccess())
  999. {
  1000. AssetProcessor::RelocationSuccess success = result.TakeValue();
  1001. // The report can be too long for the AZ_Printf buffer, so split it into individual lines
  1002. AZStd::string report = relocationInterface->BuildReport(success.m_relocationContainer, success.m_updateTasks, false, updateReferences);
  1003. AZStd::vector<AZStd::string> lines;
  1004. AzFramework::StringFunc::Tokenize(report.c_str(), lines, "\n");
  1005. for (const AZStd::string& line : lines)
  1006. {
  1007. AZ_Printf(AssetProcessor::ConsoleChannel, (line + "\n").c_str());
  1008. }
  1009. if (!previewOnly)
  1010. {
  1011. AZ_Printf(AssetProcessor::ConsoleChannel, "DELETE COMPLETE\n");
  1012. AZ_Printf(AssetProcessor::ConsoleChannel, "TOTAL FILES: %d\n", success.m_moveTotalCount);
  1013. AZ_Printf(AssetProcessor::ConsoleChannel, "SUCCESS COUNT: %d\n", success.m_moveSuccessCount);
  1014. AZ_Printf(AssetProcessor::ConsoleChannel, "FAILURE COUNT: %d\n", success.m_moveFailureCount);
  1015. }
  1016. }
  1017. else
  1018. {
  1019. AZ_Printf(AssetProcessor::ConsoleChannel, result.TakeError().c_str());
  1020. }
  1021. }
  1022. else
  1023. {
  1024. AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to retrieve ISourceFileRelocation interface");
  1025. }
  1026. AZ_Printf(AssetProcessor::ConsoleChannel, Delimiter);
  1027. }
  1028. }
  1029. bool ApplicationManagerBase::CheckFullIdle()
  1030. {
  1031. bool isIdle = m_rcController->IsIdle() && m_AssetProcessorManagerIdleState && m_remainingAssetsToFinalize == 0;
  1032. if (isIdle != m_fullIdle)
  1033. {
  1034. m_fullIdle = isIdle;
  1035. Q_EMIT FullIdle(m_fullIdle);
  1036. }
  1037. return isIdle;
  1038. }
  1039. void ApplicationManagerBase::CheckForIdle()
  1040. {
  1041. if (InitiatedShutdown())
  1042. {
  1043. return;
  1044. }
  1045. bool shouldExit = GetShouldExitOnIdle();
  1046. if (shouldExit && m_connectionsToRemoveOnShutdown.empty())
  1047. {
  1048. // we've already entered this state once. Ignore repeats. this can happen if another sender of events
  1049. // rapidly flicks between idle/not idle and sends many "I'm done!" messages which are all queued up.
  1050. return;
  1051. }
  1052. if (CheckFullIdle())
  1053. {
  1054. if (CheckReprocessFileList())
  1055. {
  1056. return;
  1057. }
  1058. if (shouldExit)
  1059. {
  1060. // If everything else is done, and it was requested to scan for missing product dependencies, perform that scan now.
  1061. TryScanProductDependencies();
  1062. TryHandleFileRelocation();
  1063. // since we are shutting down, we save the registry and then we quit.
  1064. AZ_Printf(AssetProcessor::ConsoleChannel, "No assets remain in the build queue. Saving the catalog, and then shutting down.\n");
  1065. // stop accepting any further idle messages, as we will shut down - don't want this function to repeat!
  1066. for (const QMetaObject::Connection& connection : m_connectionsToRemoveOnShutdown)
  1067. {
  1068. QObject::disconnect(connection);
  1069. }
  1070. m_connectionsToRemoveOnShutdown.clear();
  1071. // Checking the status of the asset catalog here using qt's signal slot mechanism
  1072. // to ensure that we do not have any pending events in the event loop that can make the catalog dirty again
  1073. QObject::connect(m_assetCatalog, &AssetProcessor::AssetCatalog::AsyncAssetCatalogStatusResponse, this, [&](AssetProcessor::AssetCatalogStatus status)
  1074. {
  1075. if (status == AssetProcessor::AssetCatalogStatus::RequiresSaving)
  1076. {
  1077. AssetProcessor::AssetRegistryRequestBus::Broadcast(&AssetProcessor::AssetRegistryRequests::SaveRegistry);
  1078. }
  1079. AssetProcessor::AssetRegistryRequestBus::Broadcast(&AssetProcessor::AssetRegistryRequests::ValidatePreLoadDependency);
  1080. QuitRequested();
  1081. }, Qt::UniqueConnection);
  1082. QMetaObject::invokeMethod(m_assetCatalog, "AsyncAssetCatalogStatusRequest", Qt::QueuedConnection);
  1083. }
  1084. else
  1085. {
  1086. // we save the registry when we become idle, but we stay running.
  1087. AssetProcessor::AssetRegistryRequestBus::Broadcast(&AssetProcessor::AssetRegistryRequests::SaveRegistry);
  1088. AssetProcessor::AssetRegistryRequestBus::Broadcast(&AssetProcessor::AssetRegistryRequests::ValidatePreLoadDependency);
  1089. }
  1090. }
  1091. }
  1092. WId ApplicationManagerBase::GetWindowId() const
  1093. {
  1094. return {};
  1095. }
  1096. void ApplicationManagerBase::InitBuilderManager()
  1097. {
  1098. AZ_Assert(m_connectionManager != nullptr, "ConnectionManager must be started before the builder manager");
  1099. m_builderManager = new AssetProcessor::BuilderManager(m_connectionManager);
  1100. QObject::connect(m_connectionManager, &ConnectionManager::ConnectionDisconnected, this, [this](unsigned int connId)
  1101. {
  1102. m_builderManager->ConnectionLost(connId);
  1103. });
  1104. }
  1105. void ApplicationManagerBase::ShutdownBuilderManager()
  1106. {
  1107. if (m_builderManager)
  1108. {
  1109. delete m_builderManager;
  1110. m_builderManager = nullptr;
  1111. }
  1112. }
  1113. bool ApplicationManagerBase::InitAssetDatabase(bool ignoreFutureAssetDBVersionError)
  1114. {
  1115. AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler::BusConnect();
  1116. // create or upgrade the asset database here, so that it is already good for the rest of the application and the rest
  1117. // of the application does not have to worry about a failure to upgrade or create it.
  1118. AssetProcessor::AssetDatabaseConnection database;
  1119. if (!database.OpenDatabase(ignoreFutureAssetDBVersionError))
  1120. {
  1121. return false;
  1122. }
  1123. database.CloseDatabase();
  1124. return true;
  1125. }
  1126. void ApplicationManagerBase::ShutDownAssetDatabase()
  1127. {
  1128. AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler::BusDisconnect();
  1129. }
  1130. void ApplicationManagerBase::InitFileProcessor()
  1131. {
  1132. AssetProcessor::ThreadController<AssetProcessor::FileProcessor>* fileProcessorHelper = new AssetProcessor::ThreadController<AssetProcessor::FileProcessor>();
  1133. addRunningThread(fileProcessorHelper);
  1134. m_fileProcessor.reset(fileProcessorHelper->initialize([this]()
  1135. {
  1136. return new AssetProcessor::FileProcessor(m_platformConfiguration);
  1137. }));
  1138. }
  1139. void ApplicationManagerBase::ShutDownFileProcessor()
  1140. {
  1141. m_fileProcessor.reset();
  1142. }
  1143. void ApplicationManagerBase::InitAssetServerHandler()
  1144. {
  1145. m_assetServerHandler = new AssetProcessor::AssetServerHandler();
  1146. m_assetServerHandler->HandleRemoteConfiguration();
  1147. }
  1148. void ApplicationManagerBase::DestroyAssetServerHandler()
  1149. {
  1150. delete m_assetServerHandler;
  1151. m_assetServerHandler = nullptr;
  1152. }
  1153. // IMPLEMENTATION OF -------------- AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Listener
  1154. bool ApplicationManagerBase::GetAssetDatabaseLocation(AZStd::string& location)
  1155. {
  1156. QDir cacheRoot;
  1157. if (!AssetUtilities::ComputeProjectCacheRoot(cacheRoot))
  1158. {
  1159. location = "assetdb.sqlite";
  1160. }
  1161. location = cacheRoot.absoluteFilePath("assetdb.sqlite").toUtf8().data();
  1162. return true;
  1163. }
  1164. // ------------------------------------------------------------
  1165. bool ApplicationManagerBase::CheckReprocessFileList()
  1166. {
  1167. if (m_reprocessFileList.isEmpty() && m_filesToReprocess.isEmpty())
  1168. {
  1169. return false;
  1170. }
  1171. if (!m_reprocessFileList.isEmpty())
  1172. {
  1173. QFile reprocessFile(m_reprocessFileList);
  1174. m_reprocessFileList.clear();
  1175. if (!reprocessFile.open(QIODevice::ReadOnly))
  1176. {
  1177. AZ_Error("AssetProcessor", false, "Unable to open reprocess file list with path %s.", reprocessFile.fileName().toUtf8().data());
  1178. return false;
  1179. }
  1180. while (!reprocessFile.atEnd())
  1181. {
  1182. m_filesToReprocess.append(reprocessFile.readLine());
  1183. }
  1184. reprocessFile.close();
  1185. if (m_filesToReprocess.empty())
  1186. {
  1187. AZ_Error(
  1188. "AssetProcessor", false, "No files listed to reprocess in the file at path %s.", reprocessFile.fileName().toUtf8().data());
  1189. return false;
  1190. }
  1191. }
  1192. // Queue one at a time, and wait for idle.
  1193. // This makes sure the files in the list are processed in the same order.
  1194. // Otherwise, the order can shuffle based on Asset Processor state.
  1195. m_assetProcessorManager->RequestReprocess(m_filesToReprocess.front());
  1196. m_filesToReprocess.pop_front();
  1197. return true;
  1198. }
  1199. bool ApplicationManagerBase::Activate()
  1200. {
  1201. QDir projectCache;
  1202. if (!AssetUtilities::ComputeProjectCacheRoot(projectCache))
  1203. {
  1204. AZ_Error("AssetProcessor", false, "Could not compute project cache root, please configure your project correctly to launch Asset Processor.");
  1205. return false;
  1206. }
  1207. AZ_TracePrintf(AssetProcessor::ConsoleChannel,
  1208. "AssetProcessor will process assets from project root %s.\n", AssetUtilities::ComputeProjectPath().toUtf8().data());
  1209. // Shutdown if the disk has less than 128MB of free space
  1210. if (!CheckSufficientDiskSpace(128 * 1024 * 1024, true))
  1211. {
  1212. // CheckSufficientDiskSpace reports an error if disk space is low.
  1213. return false;
  1214. }
  1215. bool appInited = InitApplicationServer();
  1216. if (!appInited)
  1217. {
  1218. AZ_Error(
  1219. "AssetProcessor", false, "InitApplicationServer failed, something internal to Asset Processor has failed, please report this to support if you encounter this error.");
  1220. return false;
  1221. }
  1222. const AzFramework::CommandLine* commandLine = nullptr;
  1223. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  1224. AZStd::vector<APCommandLineSwitch> commandLineInfo;
  1225. const APCommandLineSwitch Command_ignoreFutureDBError(commandLineInfo, "ignoreFutureAssetDatabaseVersionError", "When not set, if the Asset Processor encounters an Asset Database "
  1226. "with a future version, it will emit an error and shut down. When set, instead it will print the error as a log and erase the Asset Database, then it will proceed to initialize. "
  1227. "This is intended for use with automated builds, and shouldn't be used by individuals. If an individual finds they want to use this flag frequently, the team should "
  1228. "examine their workflows to determine why some team members encounter issues with future versioned Asset Databases.");
  1229. if (!InitAssetDatabase(commandLine->HasSwitch(Command_ignoreFutureDBError.m_switch)))
  1230. {
  1231. // AssetDatabaseConnection::OpenDatabase reports any errors it encounters.
  1232. return false;
  1233. }
  1234. if (!ApplicationManager::Activate())
  1235. {
  1236. // ApplicationManager::Activate() reports any errors it encounters.
  1237. return false;
  1238. }
  1239. if (!InitPlatformConfiguration())
  1240. {
  1241. AZ_Error("AssetProcessor", false, "Failed to Initialize from AssetProcessorPlatformConfig.setreg - check the log files in the logs/ subfolder for more information.");
  1242. return false;
  1243. }
  1244. InitBuilderConfiguration();
  1245. PopulateApplicationDependencies();
  1246. InitAssetProcessorManager(commandLineInfo);
  1247. HandleCommandLineHelp(commandLineInfo);
  1248. AssetBuilderSDK::InitializeSerializationContext();
  1249. AssetBuilderSDK::InitializeBehaviorContext();
  1250. AssetBuilder::InitializeSerializationContext();
  1251. InitFileStateCache();
  1252. InitFileProcessor();
  1253. InitUuidManager();
  1254. // now that apm, statecache, processor, and uuid manager are all alive, hook them up to the signal that AP
  1255. // gives when it modifies an intermediate asset. For the file cache, we hook it up directly so that there is
  1256. // no delay between the notification and the invalidation/creation of its cache entry.
  1257. auto notifyFileStateCache = [this](QString changedFile)
  1258. {
  1259. // the file state cache will get this immediately and inline, it needs to treat it with thread safety.
  1260. m_fileStateCache->UpdateFile(changedFile);
  1261. };
  1262. auto notifyUuidManagerAndFileProcessor = [this](QString changedFile)
  1263. {
  1264. // this is not necessarily time sensitive.
  1265. m_uuidManager->FileChanged(changedFile.toUtf8().constData());
  1266. };
  1267. QObject::connect(
  1268. m_assetProcessorManager,
  1269. &AssetProcessor::AssetProcessorManager::IntermediateAssetCreated,
  1270. this,
  1271. notifyFileStateCache,
  1272. Qt::DirectConnection);
  1273. QObject::connect(
  1274. m_assetProcessorManager,
  1275. &AssetProcessor::AssetProcessorManager::IntermediateAssetCreated,
  1276. this,
  1277. notifyUuidManagerAndFileProcessor,
  1278. Qt::QueuedConnection);
  1279. InitFileMonitor(AZStd::make_unique<FileWatcher>());
  1280. InitAssetScanner();
  1281. InitAssetServerHandler();
  1282. InitRCController();
  1283. InitConnectionManager();
  1284. InitAssetRequestHandler(new AssetProcessor::AssetRequestHandler());
  1285. InitBuilderManager();
  1286. InitSourceControl();
  1287. //We must register all objects that need to be notified if we are shutting down before we install the ctrlhandler
  1288. // inserting in the front so that the application server is notified first
  1289. // and we stop listening for new incoming connections during shutdown
  1290. RegisterObjectForQuit(m_applicationServer, true);
  1291. RegisterObjectForQuit(m_fileProcessor.get());
  1292. RegisterObjectForQuit(m_connectionManager);
  1293. RegisterObjectForQuit(m_assetProcessorManager);
  1294. RegisterObjectForQuit(m_rcController);
  1295. m_connectionsToRemoveOnShutdown << QObject::connect(
  1296. m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetProcessorManagerIdleState,
  1297. this, [this](bool state)
  1298. {
  1299. if (state)
  1300. {
  1301. QMetaObject::invokeMethod(m_rcController, "SetDispatchPaused", Qt::QueuedConnection, Q_ARG(bool, false));
  1302. }
  1303. });
  1304. m_connectionsToRemoveOnShutdown << QObject::connect(
  1305. m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetProcessorManagerIdleState,
  1306. this, &ApplicationManagerBase::OnAssetProcessorManagerIdleState);
  1307. m_connectionsToRemoveOnShutdown << QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::FinishedAnalysis, this, [this](int count)
  1308. {
  1309. m_remainingAssetsToFinalize = count;
  1310. });
  1311. m_connectionsToRemoveOnShutdown << QObject::connect(
  1312. m_rcController, &AssetProcessor::RCController::BecameIdle,
  1313. this, [this]()
  1314. {
  1315. Q_EMIT CheckAssetProcessorManagerIdleState();
  1316. });
  1317. m_connectionsToRemoveOnShutdown << QObject::connect(
  1318. this, &ApplicationManagerBase::CheckAssetProcessorManagerIdleState,
  1319. m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::CheckAssetProcessorIdleState);
  1320. MakeActivationConnections();
  1321. // only after everyones had a chance to init messages, we start listening.
  1322. if (m_applicationServer)
  1323. {
  1324. if (!m_applicationServer->startListening())
  1325. {
  1326. // startListening reports any errors it encounters.
  1327. return false;
  1328. }
  1329. }
  1330. AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Initializing_Builders, 0, QString());
  1331. Q_EMIT AssetProcessorStatusChanged(entry);
  1332. // Start up a thread which will request a builder to start to handle the registration of gems/builders
  1333. // Builder info will be sent back to the AP via the network connection, start up will wait for the info before continuing
  1334. // See ApplicationManagerBase::InitConnectionManager BuilderRegistrationRequest for the resume point here
  1335. // Waiting here is not possible because the message comes back as a network message, which requires the main thread to process it
  1336. // Since execution has to continue, this also means the thread object will go out of scope, so it must be detached before exiting.
  1337. AZStd::thread_desc desc;
  1338. desc.m_name = "Builder Component Registration";
  1339. AZStd::thread builderRegistrationThread(
  1340. desc,
  1341. []()
  1342. {
  1343. AssetProcessor::BuilderRef builder;
  1344. AssetProcessor::BuilderManagerBus::BroadcastResult(builder, &AssetProcessor::BuilderManagerBus::Events::GetBuilder, AssetProcessor::BuilderPurpose::Registration);
  1345. if (!builder)
  1346. {
  1347. AZ_Error("ApplicationManagerBase", false, "AssetBuilder process failed to start. Builder registration cannot complete. Shutting down.");
  1348. AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::OnBuilderRegistrationFailure);
  1349. }
  1350. });
  1351. builderRegistrationThread.detach();
  1352. // While waiting for builder registration, start scanning
  1353. GetAssetScanner()->StartScan();
  1354. return true;
  1355. }
  1356. bool ApplicationManagerBase::PostActivate()
  1357. {
  1358. m_connectionManager->LoadConnections();
  1359. InitializeInternalBuilders();
  1360. Q_EMIT OnBuildersRegistered();
  1361. // 25 milliseconds is above the 'while loop' thing that QT does on windows (where small time ticks will spin loop instead of sleep)
  1362. m_ticker = new AzToolsFramework::Ticker(nullptr, 25.0f);
  1363. m_ticker->Start();
  1364. connect(m_ticker, &AzToolsFramework::Ticker::Tick, this, []()
  1365. {
  1366. AZ::SystemTickBus::ExecuteQueuedEvents();
  1367. AZ::SystemTickBus::Broadcast(&AZ::SystemTickEvents::OnSystemTick);
  1368. });
  1369. return true;
  1370. }
  1371. void ApplicationManagerBase::Reflect()
  1372. {
  1373. AZ::SerializeContext* context = nullptr;
  1374. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  1375. AZ_Assert(context, "SerializeContext is not available");
  1376. ApplicationManager::Reflect();
  1377. AssetProcessor::UuidManager::Reflect(context);
  1378. }
  1379. void ApplicationManagerBase::CreateQtApplication()
  1380. {
  1381. m_qApp = new QCoreApplication(*m_frameworkApp.GetArgC(), *m_frameworkApp.GetArgV());
  1382. }
  1383. bool ApplicationManagerBase::InitializeInternalBuilders()
  1384. {
  1385. m_internalBuilder = AZStd::make_shared<AssetProcessor::InternalRecognizerBasedBuilder>();
  1386. bool result = m_internalBuilder->Initialize(*this->m_platformConfiguration);
  1387. m_settingsRegistryBuilder = AZStd::make_shared<AssetProcessor::SettingsRegistryBuilder>();
  1388. result = m_settingsRegistryBuilder->Initialize() && result;
  1389. return result;
  1390. }
  1391. static void HandleConditionalRetry(const AssetProcessor::BuilderRunJobOutcome& result, int retryCount, AssetProcessor::BuilderRef& builderRef, AssetProcessor::BuilderPurpose purpose)
  1392. {
  1393. // If a lost connection occured or the process was terminated before a response can be read, and there is another retry to get the
  1394. // response from a Builder, then handle the logic to log and sleep before attempting the retry of the job
  1395. if ((result == AssetProcessor::BuilderRunJobOutcome::LostConnection ||
  1396. result == AssetProcessor::BuilderRunJobOutcome::ProcessTerminated ) && (retryCount <= AssetProcessor::RetriesForJobLostConnection))
  1397. {
  1398. const int delay = 1 << (retryCount-1);
  1399. // Check if we need a new builder, and if so, request a new one
  1400. if (!builderRef->IsValid())
  1401. {
  1402. // If the connection was lost and the process handle is no longer valid, then we need to request a new builder to reprocess the job
  1403. AZStd::string oldBuilderId = builderRef->GetUuid().ToString<AZStd::string>();
  1404. builderRef.release();
  1405. AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder, purpose);
  1406. if (builderRef)
  1407. {
  1408. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Lost connection to builder %s. Retrying with a new builder %s (Attempt %d with %d second delay)",
  1409. oldBuilderId.c_str(),
  1410. builderRef->GetUuid().ToString<AZStd::string>().c_str(),
  1411. retryCount+1,
  1412. delay);
  1413. }
  1414. else
  1415. {
  1416. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Lost connection to builder %s and no further builders are available. Job will not retry.\n",
  1417. oldBuilderId.c_str());
  1418. // if we failed to get a builder ref, it means we're probably
  1419. // shutting down, in which case we do not want to do an exponential
  1420. // backoff delay and need to return immediately.
  1421. return;
  1422. }
  1423. }
  1424. else
  1425. {
  1426. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Lost connection to builder %s. Retrying (Attempt %d with %d second delay)",
  1427. builderRef->GetUuid().ToString<AZStd::string>().c_str(),
  1428. retryCount+1,
  1429. delay);
  1430. }
  1431. AZStd::this_thread::sleep_for(AZStd::chrono::seconds(delay));
  1432. }
  1433. }
  1434. void ApplicationManagerBase::RegisterBuilderInformation(const AssetBuilderSDK::AssetBuilderDesc& builderDesc)
  1435. {
  1436. if (!builderDesc.IsExternalBuilder())
  1437. {
  1438. // Create Job Function validation
  1439. AZ_Error(
  1440. AssetProcessor::ConsoleChannel, builderDesc.m_createJobFunction,
  1441. "Create Job Function (m_createJobFunction) for %s builder is empty.\n", builderDesc.m_name.c_str());
  1442. // Process Job Function validation
  1443. AZ_Error(
  1444. AssetProcessor::ConsoleChannel, builderDesc.m_processJobFunction,
  1445. "Process Job Function (m_processJobFunction) for %s builder is empty.\n", builderDesc.m_name.c_str());
  1446. }
  1447. // Bus ID validation
  1448. AZ_Error(AssetProcessor::ConsoleChannel,
  1449. !builderDesc.m_busId.IsNull(),
  1450. "Bus ID for %s builder is empty.\n",
  1451. builderDesc.m_name.c_str());
  1452. AssetBuilderSDK::AssetBuilderDesc modifiedBuilderDesc = builderDesc;
  1453. // Allow for overrides defined in a BuilderConfig.ini file to update our code defined default values
  1454. AssetProcessor::BuilderConfigurationRequestBus::Broadcast(&AssetProcessor::BuilderConfigurationRequests::UpdateBuilderDescriptor, builderDesc.m_name, modifiedBuilderDesc);
  1455. if (builderDesc.IsExternalBuilder())
  1456. {
  1457. // We're going to override the createJob function so we can run it externally in AssetBuilder, rather than having it run
  1458. // inside the AP
  1459. modifiedBuilderDesc.m_createJobFunction =
  1460. [this](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
  1461. {
  1462. AssetProcessor::BuilderRef builderRef;
  1463. AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder, AssetProcessor::BuilderPurpose::CreateJobs);
  1464. if (builderRef)
  1465. {
  1466. int retryCount = 0;
  1467. AssetProcessor::BuilderRunJobOutcome result;
  1468. if (this->InitiatedShutdown())
  1469. {
  1470. return; // exit early if you're shutting down!
  1471. }
  1472. do
  1473. {
  1474. retryCount++;
  1475. result = builderRef->RunJob<AssetBuilder::CreateJobsNetRequest, AssetBuilder::CreateJobsNetResponse>(
  1476. request, response, s_MaximumCreateJobsTimeSeconds, "create", "", nullptr);
  1477. HandleConditionalRetry(result, retryCount, builderRef, AssetProcessor::BuilderPurpose::CreateJobs);
  1478. } while ((result == AssetProcessor::BuilderRunJobOutcome::LostConnection ||
  1479. result == AssetProcessor::BuilderRunJobOutcome::ProcessTerminated) &&
  1480. retryCount <= AssetProcessor::RetriesForJobLostConnection);
  1481. }
  1482. else
  1483. {
  1484. AZ_Error("AssetProcessor", false, "Failed to retrieve a valid builder to process job");
  1485. }
  1486. };
  1487. const bool debugOutput = m_assetProcessorManager->GetBuilderDebugFlag();
  1488. // Also override the processJob function to run externally
  1489. modifiedBuilderDesc.m_processJobFunction =
  1490. [this, debugOutput](const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  1491. {
  1492. AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
  1493. AssetProcessor::BuilderRef builderRef;
  1494. AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder, AssetProcessor::BuilderPurpose::ProcessJob);
  1495. if (builderRef)
  1496. {
  1497. if (debugOutput)
  1498. {
  1499. AssetProcessor::BuilderManagerBus::Broadcast(
  1500. &AssetProcessor::BuilderManagerBusTraits::AddAssetToBuilderProcessedList, builderRef->GetUuid(),
  1501. request.m_fullPath);
  1502. }
  1503. int retryCount = 0;
  1504. AssetProcessor::BuilderRunJobOutcome result;
  1505. do
  1506. {
  1507. if (jobCancelListener.IsCancelled())
  1508. {
  1509. // do not attempt to continue to retry or spawn
  1510. // new builders during shut down.
  1511. break;
  1512. }
  1513. if (this->InitiatedShutdown())
  1514. {
  1515. return; // exit early if you're shutting down!
  1516. }
  1517. retryCount++;
  1518. result = builderRef->RunJob<AssetBuilder::ProcessJobNetRequest, AssetBuilder::ProcessJobNetResponse>(
  1519. request, response, s_MaximumProcessJobsTimeSeconds, "process", "", &jobCancelListener, request.m_tempDirPath);
  1520. HandleConditionalRetry(result, retryCount, builderRef, AssetProcessor::BuilderPurpose::ProcessJob);
  1521. } while ((result == AssetProcessor::BuilderRunJobOutcome::LostConnection ||
  1522. result == AssetProcessor::BuilderRunJobOutcome::ProcessTerminated) &&
  1523. retryCount <= AssetProcessor::RetriesForJobLostConnection);
  1524. }
  1525. else
  1526. {
  1527. AZ_Error("AssetProcessor", false, "Failed to retrieve a valid builder to process job");
  1528. }
  1529. };
  1530. }
  1531. if (m_builderDescMap.find(modifiedBuilderDesc.m_busId) != m_builderDescMap.end())
  1532. {
  1533. AZ_Warning(AssetProcessor::DebugChannel, false, "Uuid for %s builder is already registered.\n", modifiedBuilderDesc.m_name.c_str());
  1534. return;
  1535. }
  1536. if (m_builderNameToId.find(modifiedBuilderDesc.m_name) != m_builderNameToId.end())
  1537. {
  1538. AZ_Warning(AssetProcessor::DebugChannel, false, "Duplicate builder detected. A builder named '%s' is already registered.\n", modifiedBuilderDesc.m_name.c_str());
  1539. return;
  1540. }
  1541. AZStd::sort(modifiedBuilderDesc.m_patterns.begin(), modifiedBuilderDesc.m_patterns.end(),
  1542. [](const AssetBuilderSDK::AssetBuilderPattern& first, const AssetBuilderSDK::AssetBuilderPattern& second)
  1543. {
  1544. return first.ToString() < second.ToString();
  1545. });
  1546. m_builderDescMap[modifiedBuilderDesc.m_busId] = modifiedBuilderDesc;
  1547. m_builderNameToId[modifiedBuilderDesc.m_name] = modifiedBuilderDesc.m_busId;
  1548. for (const AssetBuilderSDK::AssetBuilderPattern& pattern : modifiedBuilderDesc.m_patterns)
  1549. {
  1550. AssetUtilities::BuilderFilePatternMatcher patternMatcher(pattern, modifiedBuilderDesc.m_busId);
  1551. m_matcherBuilderPatterns.push_back(patternMatcher);
  1552. }
  1553. }
  1554. void ApplicationManagerBase::RegisterComponentDescriptor(AZ::ComponentDescriptor* descriptor)
  1555. {
  1556. ApplicationManager::RegisterComponentDescriptor(descriptor);
  1557. if (m_currentExternalAssetBuilder)
  1558. {
  1559. m_currentExternalAssetBuilder->RegisterComponentDesc(descriptor);
  1560. }
  1561. else
  1562. {
  1563. AZ_Warning(AssetProcessor::DebugChannel, false, "Component description can only be registered during component activation.\n");
  1564. }
  1565. }
  1566. void ApplicationManagerBase::BuilderLog(const AZ::Uuid& builderId, const char* message, ...)
  1567. {
  1568. va_list args;
  1569. va_start(args, message);
  1570. BuilderLogV(builderId, message, args);
  1571. va_end(args);
  1572. }
  1573. void ApplicationManagerBase::BuilderLogV(const AZ::Uuid& builderId, const char* message, va_list list)
  1574. {
  1575. AZStd::string builderName;
  1576. if (m_builderDescMap.find(builderId) != m_builderDescMap.end())
  1577. {
  1578. char messageBuffer[1024];
  1579. azvsnprintf(messageBuffer, 1024, message, list);
  1580. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Builder name : %s Message : %s.\n", m_builderDescMap[builderId].m_name.c_str(), messageBuffer);
  1581. }
  1582. else
  1583. {
  1584. // asset processor does not know about this builder id
  1585. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor does not know about the builder id: %s. \n", builderId.ToString<AZStd::string>().c_str());
  1586. }
  1587. }
  1588. bool ApplicationManagerBase::FindBuilderInformation(const AZ::Uuid& builderGuid, AssetBuilderSDK::AssetBuilderDesc& descriptionOut)
  1589. {
  1590. auto iter = m_builderDescMap.find(builderGuid);
  1591. if (iter != m_builderDescMap.end())
  1592. {
  1593. descriptionOut = iter->second;
  1594. return true;
  1595. }
  1596. else
  1597. {
  1598. return false;
  1599. }
  1600. }
  1601. void ApplicationManagerBase::UnRegisterBuilderDescriptor(const AZ::Uuid& builderId)
  1602. {
  1603. if (m_builderDescMap.find(builderId) == m_builderDescMap.end())
  1604. {
  1605. AZ_Warning(AssetProcessor::DebugChannel, false, "Cannot unregister builder descriptor for Uuid %s, not currently registered.\n", builderId.ToString<AZStd::string>().c_str());
  1606. return;
  1607. }
  1608. // Remove from the map
  1609. AssetBuilderSDK::AssetBuilderDesc& descToUnregister = m_builderDescMap[builderId];
  1610. AZStd::string descNameToUnregister = descToUnregister.m_name;
  1611. descToUnregister.m_createJobFunction.clear();
  1612. descToUnregister.m_processJobFunction.clear();
  1613. m_builderDescMap.erase(builderId);
  1614. m_builderNameToId.erase(descNameToUnregister);
  1615. // Remove the matcher build pattern
  1616. for (auto remover = this->m_matcherBuilderPatterns.begin();
  1617. remover != this->m_matcherBuilderPatterns.end();
  1618. remover++)
  1619. {
  1620. if (remover->GetBuilderDescID() == builderId)
  1621. {
  1622. auto deleteIter = remover;
  1623. remover++;
  1624. this->m_matcherBuilderPatterns.erase(deleteIter);
  1625. }
  1626. }
  1627. }
  1628. void ApplicationManagerBase::GetMatchingBuildersInfo(const AZStd::string& assetPath, AssetProcessor::BuilderInfoList& builderInfoList)
  1629. {
  1630. AZStd::set<AZ::Uuid> uniqueBuilderDescIDs;
  1631. for (AssetUtilities::BuilderFilePatternMatcher& matcherPair : m_matcherBuilderPatterns)
  1632. {
  1633. if (uniqueBuilderDescIDs.find(matcherPair.GetBuilderDescID()) != uniqueBuilderDescIDs.end())
  1634. {
  1635. continue;
  1636. }
  1637. if (matcherPair.MatchesPath(assetPath))
  1638. {
  1639. const AssetBuilderSDK::AssetBuilderDesc& builderDesc = m_builderDescMap[matcherPair.GetBuilderDescID()];
  1640. uniqueBuilderDescIDs.insert(matcherPair.GetBuilderDescID());
  1641. builderInfoList.push_back(builderDesc);
  1642. }
  1643. }
  1644. }
  1645. void ApplicationManagerBase::GetAllBuildersInfo(AssetProcessor::BuilderInfoList& builderInfoList)
  1646. {
  1647. for (const auto &builderPair : m_builderDescMap)
  1648. {
  1649. builderInfoList.push_back(builderPair.second);
  1650. }
  1651. }
  1652. bool ApplicationManagerBase::OnError(const char* /*window*/, const char* /*message*/)
  1653. {
  1654. // We don't need to print the message to stdout, the trace system will already do that
  1655. return true;
  1656. }
  1657. bool ApplicationManagerBase::CheckSufficientDiskSpace(qint64 requiredSpace, bool shutdownIfInsufficient)
  1658. {
  1659. QDir cacheDir;
  1660. if (!AssetUtilities::ComputeProjectCacheRoot(cacheDir))
  1661. {
  1662. AZ_Error(
  1663. "AssetProcessor",
  1664. false,
  1665. "Could not compute project cache root, please configure your project correctly to launch Asset Processor.");
  1666. return false;
  1667. }
  1668. QString savePath = cacheDir.absolutePath();
  1669. if (!QDir(savePath).exists())
  1670. {
  1671. // GetFreeDiskSpace will fail if the path does not exist
  1672. QDir dir;
  1673. dir.mkpath(savePath);
  1674. }
  1675. qint64 bytesFree = 0;
  1676. [[maybe_unused]] bool result = AzToolsFramework::ToolsFileUtils::GetFreeDiskSpace(savePath, bytesFree);
  1677. AZ_Assert(result, "Unable to determine the amount of free space on drive containing path (%s).", savePath.toUtf8().constData());
  1678. if (bytesFree < requiredSpace + s_ReservedDiskSpaceInBytes)
  1679. {
  1680. if (shutdownIfInsufficient)
  1681. {
  1682. AZ_Error(AssetProcessor::ConsoleChannel, false, "There is insufficient disk space to continue running. AssetProcessor will now exit");
  1683. QMetaObject::invokeMethod(this, "QuitRequested", Qt::QueuedConnection);
  1684. }
  1685. return false;
  1686. }
  1687. return true;
  1688. }
  1689. void ApplicationManagerBase::RemoveOldTempFolders()
  1690. {
  1691. QDir rootDir;
  1692. if (!AssetUtilities::ComputeAssetRoot(rootDir))
  1693. {
  1694. return;
  1695. }
  1696. QString startFolder;
  1697. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  1698. {
  1699. if (AZ::IO::Path userPath; settingsRegistry->Get(userPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
  1700. {
  1701. startFolder = QString::fromUtf8(userPath.c_str(), aznumeric_cast<int>(userPath.Native().size()));
  1702. }
  1703. }
  1704. QDir root;
  1705. if (!AssetUtilities::CreateTempRootFolder(startFolder, root))
  1706. {
  1707. return;
  1708. }
  1709. // We will remove old temp folders if either their modified time is older than the cutoff time or
  1710. // if the total number of temp folders have exceeded the maximum number of temp folders.
  1711. QFileInfoList entries = root.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time); // sorting by modification time
  1712. int folderCount = 0;
  1713. bool removeFolder = false;
  1714. QDateTime cutoffTime = QDateTime::currentDateTime().addDays(-7);
  1715. for (const QFileInfo& entry : entries)
  1716. {
  1717. if (!entry.fileName().startsWith("JobTemp-"))
  1718. {
  1719. continue;
  1720. }
  1721. // Since we are sorting the folders list from latest to oldest, we will either be in a state where we have to delete all the remaining folders or not
  1722. // because either we have reached the folder limit or reached the cutoff date limit.
  1723. removeFolder = removeFolder || (folderCount++ >= s_MaximumTempFolders) ||
  1724. (entry.lastModified() < cutoffTime);
  1725. if (removeFolder)
  1726. {
  1727. QDir dir(entry.absoluteFilePath());
  1728. dir.removeRecursively();
  1729. }
  1730. }
  1731. }
  1732. void ApplicationManagerBase::ConnectivityStateChanged(const AzToolsFramework::SourceControlState /*newState*/)
  1733. {
  1734. Q_EMIT SourceControlReady();
  1735. }
  1736. void ApplicationManagerBase::OnBuilderRegistrationFailure()
  1737. {
  1738. QMetaObject::invokeMethod(this, "QuitRequested");
  1739. }
  1740. void ApplicationManagerBase::OnAssetProcessorManagerIdleState(bool isIdle)
  1741. {
  1742. // these can come in during shutdown.
  1743. if (InitiatedShutdown())
  1744. {
  1745. return;
  1746. }
  1747. if (isIdle)
  1748. {
  1749. if (!m_AssetProcessorManagerIdleState)
  1750. {
  1751. // We want to again ask the APM for the idle state just incase it goes from idle to non idle in between
  1752. Q_EMIT CheckAssetProcessorManagerIdleState();
  1753. }
  1754. else
  1755. {
  1756. CheckForIdle();
  1757. return;
  1758. }
  1759. }
  1760. if (isIdle != m_AssetProcessorManagerIdleState)
  1761. {
  1762. Q_EMIT AssetProcesserManagerIdleStateChange(isIdle);
  1763. }
  1764. m_AssetProcessorManagerIdleState = isIdle;
  1765. }
  1766. bool ApplicationManagerBase::IsAssetProcessorManagerIdle() const
  1767. {
  1768. return m_AssetProcessorManagerIdleState;
  1769. }
  1770. void ApplicationManagerBase::OnActiveJobsCountChanged(unsigned int count)
  1771. {
  1772. AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Processing_Jobs, count);
  1773. Q_EMIT AssetProcessorStatusChanged(entry);
  1774. }