assetProcessorManager.h 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  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. #pragma once
  9. #if !defined(Q_MOC_RUN)
  10. #include <QString>
  11. #include <QByteArray>
  12. #include <QQueue>
  13. #include <QVector>
  14. #include <QHash>
  15. #include <QDir>
  16. #include <QSet>
  17. #include <QMap>
  18. #include <QPair>
  19. #include <QMutex>
  20. #include <AzCore/std/smart_ptr/shared_ptr.h>
  21. #include <AzCore/std/containers/unordered_map.h>
  22. #include <AssetBuilderSDK/AssetBuilderSDK.h>
  23. #include <AssetBuilderSDK/AssetBuilderBusses.h>
  24. #include "native/assetprocessor.h"
  25. #include "native/utilities/AssetUtilEBusHelper.h"
  26. #include "native/utilities/MissingDependencyScanner.h"
  27. #include "native/utilities/ThreadHelper.h"
  28. #include "native/AssetManager/AssetCatalog.h"
  29. #include "native/AssetDatabase/AssetDatabase.h"
  30. #include <AzCore/std/containers/unordered_set.h>
  31. #include <AzCore/std/containers/map.h>
  32. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  33. #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
  34. #include "AssetRequestHandler.h"
  35. #include "native/utilities/JobDiagnosticTracker.h"
  36. #include "SourceFileRelocator.h"
  37. #include <AssetManager/ExcludedFolderCache.h>
  38. #include <AssetManager/ProductAsset.h>
  39. #include <native/utilities/IMetadataUpdates.h>
  40. #endif
  41. class FileWatcher;
  42. namespace AzFramework
  43. {
  44. namespace AssetSystem
  45. {
  46. class BaseAssetProcessorMessage;
  47. class GetRelativeProductPathFromFullSourceOrProductPathRequest;
  48. class GetRelativeProductPathFromFullSourceOrProductPathResponse;
  49. class GenerateRelativeSourcePathRequest;
  50. class GenerateRelativeSourcePathResponse;
  51. class GetFullSourcePathFromRelativeProductPathRequest;
  52. class GetFullSourcePathFromRelativeProductPathResponse;
  53. class AssetNotificationMessage;
  54. } // namespace AssetSystem
  55. } // namespace AzFramework
  56. namespace AzToolsFramework
  57. {
  58. namespace AssetSystem
  59. {
  60. class AssetJobLogRequest;
  61. class AssetJobLogResponse;
  62. class AssetFingerprintClearRequest;
  63. class AssetFingerprintClearResponse;
  64. class AssetJobsInfoRequest;
  65. class AssetJobsInfoResponse;
  66. class GetAbsoluteAssetDatabaseLocationRequest;
  67. class GetAbsoluteAssetDatabaseLocationResponse;
  68. } // namespace AssetSystem
  69. } // namespace AzToolsFramework
  70. namespace AssetProcessor
  71. {
  72. class AssetProcessingStateData;
  73. struct AssetRecognizer;
  74. class PlatformConfiguration;
  75. class ScanFolderInfo;
  76. class PathDependencyManager;
  77. class LfsPointerFileValidator;
  78. //! The Asset Processor Manager is the heart of the pipeline
  79. //! It is what makes the critical decisions about what should and should not be processed
  80. //! It emits signals when jobs need to be performed and when assets are complete or have failed.
  81. class AssetProcessorManager
  82. : public QObject
  83. , public AssetProcessor::ProcessingJobInfoBus::Handler
  84. , public AZ::Interface<AssetProcessor::IMetadataUpdates>::Registrar
  85. {
  86. using BaseAssetProcessorMessage = AzFramework::AssetSystem::BaseAssetProcessorMessage;
  87. using AssetFingerprintClearRequest = AzToolsFramework::AssetSystem::AssetFingerprintClearRequest;
  88. using AssetFingerprintClearResponse = AzToolsFramework::AssetSystem::AssetFingerprintClearResponse;
  89. using AssetJobsInfoRequest = AzToolsFramework::AssetSystem::AssetJobsInfoRequest;
  90. using AssetJobsInfoResponse = AzToolsFramework::AssetSystem::AssetJobsInfoResponse;
  91. using JobInfo = AzToolsFramework::AssetSystem::JobInfo;
  92. using JobStatus = AzToolsFramework::AssetSystem::JobStatus;
  93. using AssetJobLogRequest = AzToolsFramework::AssetSystem::AssetJobLogRequest;
  94. using AssetJobLogResponse = AzToolsFramework::AssetSystem::AssetJobLogResponse;
  95. using GetAbsoluteAssetDatabaseLocationRequest = AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationRequest;
  96. using GetAbsoluteAssetDatabaseLocationResponse = AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationResponse;
  97. using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest;
  98. using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse;
  99. using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest;
  100. using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse;
  101. using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest;
  102. using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse;
  103. Q_OBJECT
  104. private:
  105. struct FileEntry
  106. {
  107. QString m_fileName;
  108. bool m_isDelete = false;
  109. bool m_isFromScanner = false;
  110. AZStd::chrono::steady_clock::time_point m_initialProcessTime{};
  111. FileEntry() = default;
  112. FileEntry(const QString& fileName, bool isDelete, bool isFromScanner = false, AZStd::chrono::steady_clock::time_point initialProcessTime = {})
  113. : m_fileName(fileName)
  114. , m_isDelete(isDelete)
  115. , m_isFromScanner(isFromScanner)
  116. , m_initialProcessTime(initialProcessTime)
  117. {
  118. }
  119. };
  120. struct AssetProcessedEntry
  121. {
  122. JobEntry m_entry;
  123. AssetBuilderSDK::ProcessJobResponse m_response;
  124. AssetProcessedEntry() = default;
  125. AssetProcessedEntry(JobEntry& entry, AssetBuilderSDK::ProcessJobResponse& response)
  126. : m_entry(AZStd::move(entry))
  127. , m_response(AZStd::move(response))
  128. {
  129. }
  130. AssetProcessedEntry(const AssetProcessedEntry& other) = default;
  131. AssetProcessedEntry(AssetProcessedEntry&& other)
  132. : m_entry(AZStd::move(other.m_entry))
  133. , m_response(AZStd::move(other.m_response))
  134. {
  135. }
  136. AssetProcessedEntry& operator=(AssetProcessedEntry&& other)
  137. {
  138. if (this != &other)
  139. {
  140. m_entry = AZStd::move(other.m_entry);
  141. m_response = AZStd::move(other.m_response);
  142. }
  143. return *this;
  144. }
  145. };
  146. //! Internal structure that will hold all the necessary source info
  147. struct SourceFileInfo
  148. {
  149. SourceAssetReference m_sourceAssetReference;
  150. AZ::Uuid m_uuid;
  151. const ScanFolderInfo* m_scanFolder{ nullptr };
  152. };
  153. public:
  154. explicit AssetProcessorManager(AssetProcessor::PlatformConfiguration* config, QObject* parent = nullptr);
  155. virtual ~AssetProcessorManager();
  156. bool IsIdle();
  157. bool HasProcessedCriticalAssets() const;
  158. //////////////////////////////////////////////////////////////////////////
  159. // ProcessingJobInfoBus::Handler overrides
  160. void BeginCacheFileUpdate(const char* productPath) override;
  161. void EndCacheFileUpdate(const char* productPath, bool queueAgainForDeletion) override;
  162. AZ::u32 GetJobFingerprint(const AssetProcessor::JobIndentifier& jobIndentifier) override;
  163. //////////////////////////////////////////////////////////////////////////
  164. //! Controls whether or not we are allowed to skip analysis on a file when the source files modtimes have not changed
  165. //! and neither have any builders.
  166. void SetEnableModtimeSkippingFeature(bool enable);
  167. bool GetModtimeSkippingFeatureEnabled() const;
  168. //! Controls whether or not startup analysis is enabled or not.
  169. void SetInitialScanSkippingFeature(bool enable);
  170. bool GetInitialScanSkippingFeatureEnabled() const;
  171. //! Query logging will log every asset database query.
  172. void SetQueryLogging(bool enableLogging);
  173. void SetBuilderDebugFlag(bool enabled);
  174. bool GetBuilderDebugFlag() const { return m_builderDebugFlag; }
  175. //! Scans assets that match the given pattern for content that looks like a missing product dependency.
  176. //! Note that the database pattern is used as an SQL query, so use SQL syntax for the search (wildcard is %, not *).
  177. //! FilePattern is just a normal wildcard pattern that can be used to filter files in the provided scan folders.
  178. void ScanForMissingProductDependencies(QString dbPattern, QString filePattern, const AZStd::vector<AZStd::string>& dependencyAdditionalScanFolders, int maxScanIteration=AssetProcessor::MissingDependencyScanner::DefaultMaxScanIteration);
  179. AZStd::shared_ptr<AssetDatabaseConnection> GetDatabaseConnection() const;
  180. void EmitResolvedDependency(const AZ::Data::AssetId& assetId, const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry);
  181. //! Internal structure that will hold all the necessary information to process jobs later.
  182. //! We need to hold these jobs because they have declared either source dependency on other sources
  183. //! or a job dependency and we can only resolve these dependencies once all the create jobs are completed.
  184. struct JobToProcessEntry
  185. {
  186. bool operator<(const JobToProcessEntry& other)
  187. {
  188. return m_sourceFileInfo.m_sourceAssetReference.RelativePath() < other.m_sourceFileInfo.m_sourceAssetReference.RelativePath();
  189. }
  190. SourceFileInfo m_sourceFileInfo;
  191. AZStd::vector<JobDetails> m_jobsToAnalyze;
  192. // a vector of pairs of <builder which emitted it, the dependency>
  193. AZStd::vector<AZStd::pair<AZ::Uuid, AssetBuilderSDK::SourceFileDependency>> m_sourceFileDependencies;
  194. };
  195. //! Request to invalidate and reprocess a source asset or folder containing source assets
  196. AZ::u64 RequestReprocess(const QString& sourcePath);
  197. AZ::u64 RequestReprocess(const AZStd::list<AZStd::string>& reprocessList);
  198. //! Retrieves the scan folder ID for the intermediate asset scan folder, if available.
  199. //! Calls GetIntermediateAssetsScanFolderId for the platform config, which returns an optional.
  200. //! If the scan folder ID is not available, returns nullopt, otherwise returns the scan folder ID.
  201. //! The scan folder ID may not be available if the platform config is not available,
  202. //! or the scan folder ID hasn't been set for the platform config.
  203. AZStd::optional<AZ::s64> GetIntermediateAssetScanFolderId() const;
  204. //! Sets the maximum amount of time to wait before automatically generating a metadata file for an asset which does not currently have a metadata file.
  205. //! Only applies to file types which are using the metadata system.
  206. //! This is used to prevent AP generating new metadata files while someone is trying to rename an existing file.
  207. void SetMetaCreationDelay(AZ::u32 milliseconds);
  208. //! Gets the maximum amount of time to wait before generating a metadata file.
  209. AZ::u32 GetMetaCreationDelay() const { return m_metaCreationDelayMs; }
  210. void PrepareForFileMove(AZ::IO::PathView oldPath, AZ::IO::PathView newPath) override;
  211. Q_SIGNALS:
  212. void NumRemainingJobsChanged(int newNumJobs);
  213. void AssetToProcess(JobDetails jobDetails);
  214. //! Emit whenever a new asset is found or an existing asset is updated
  215. void AssetMessage(AzFramework::AssetSystem::AssetNotificationMessage message);
  216. // InputAssetProcessed - uses absolute asset path of input file
  217. void InputAssetProcessed(QString fullAssetPath, QString platform);
  218. void RequestInputAssetStatus(QString inputAssetPath, QString platform, QString jobDescription);
  219. void RequestPriorityAssetCompile(QString inputAssetPath, QString platform, QString jobDescription);
  220. //! AssetProcessorManagerIdleState is emitted when APM idle state changes, we emit true when
  221. //! APM is waiting for outside stimulus i.e its has eaten through all of its queues and is only waiting for
  222. //! responses back from other systems (like its waiting for responses back from the compiler)
  223. void AssetProcessorManagerIdleState(bool state);
  224. void ReadyToQuit(QObject* source);
  225. void CreateAssetsRequest(unsigned int nonce, QString name, QString platform, bool onlyExactMatch = true, bool syncRequest = false);
  226. void SendAssetExistsResponse(NetworkRequestID groupID, bool exists);
  227. void FenceFileDetected(unsigned int fenceId);
  228. void EscalateJobs(AssetProcessor::JobIdEscalationList jobIdEscalationList);
  229. void SourceDeleted(SourceAssetReference sourceAsset);
  230. void SourceFolderDeleted(QString folderPath);
  231. void SourceQueued(AZ::Uuid sourceUuid, AZStd::unordered_set<AZ::Uuid> legacyUuids, SourceAssetReference sourceAssetReference);
  232. void SourceFinished(AZ::Uuid sourceUuid, AZStd::unordered_set<AZ::Uuid> legacyUuids);
  233. void JobRemoved(AzToolsFramework::AssetSystem::JobInfo jobInfo);
  234. void JobComplete(JobEntry jobEntry, AzToolsFramework::AssetSystem::JobStatus status);
  235. void JobProcessDurationChanged(JobEntry jobEntry, int durationMs);
  236. void CreateJobsDurationChanged(QString sourceName, AZ::s64 scanFolderID);
  237. void IntermediateAssetCreated(QString newFileAbsolutePath);
  238. //! Send a message when a new path dependency is resolved, so that downstream tools know the AssetId of the resolved dependency.
  239. void PathDependencyResolved(const AZ::Data::AssetId& assetId, const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry);
  240. void AddedToCatalog(JobEntry jobEntry);
  241. //! Fired when FinishAnalysis is run for a file to notify that a source file has completely finished processing.
  242. //! count is the number of files remaining waiting for FinishAnalysis to be called
  243. void FinishedAnalysis(int count);
  244. //! Fired when processing of a file has been delayed to wait for a metadata file creation event.
  245. void ProcessingDelayed(QString filePath);
  246. //! Fired when a previously-delayed file has begun processing.
  247. void ProcessingResumed(QString filePath);
  248. void FileCacheIsReady();
  249. public Q_SLOTS:
  250. void AssetProcessed(JobEntry jobEntry, AssetBuilderSDK::ProcessJobResponse response);
  251. void AssetProcessed_Impl();
  252. void HandleSourceUuidChange(AzToolsFramework::AssetDatabase::SourceDatabaseEntry& source, AZ::Uuid newUuid);
  253. void AssetFailed(JobEntry jobEntry);
  254. void AssetCancelled(JobEntry jobEntry);
  255. void AssessFilesFromScanner(QSet<AssetFileInfo> filePaths);
  256. void RecordFilesFromScanner(QSet<AssetFileInfo> filePaths);
  257. void RecordFoldersFromScanner(QSet<AssetFileInfo> folderPaths);
  258. void RecordExcludesFromScanner(QSet<AssetFileInfo> excludePaths);
  259. virtual void AssessModifiedFile(QString filePath);
  260. virtual void AssessAddedFile(QString filePath);
  261. virtual void AssessDeletedFile(QString filePath);
  262. void OnAssetScannerStatusChange(AssetProcessor::AssetScanningStatus status);
  263. void FinishAssetScan();
  264. void OnJobStatusChanged(JobEntry jobEntry, JobStatus status);
  265. void CheckAssetProcessorIdleState();
  266. void QuitRequested();
  267. //! A network request to clear the fingerprint for a given asset, so that the next time the timestamp changes, the file will re-process.
  268. AssetFingerprintClearResponse ProcessFingerprintClearRequest(MessageData<AssetFingerprintClearRequest> messageData);
  269. //! A network request came in asking, for a given input asset, what the status is of any jobs related to that request
  270. AssetJobsInfoResponse ProcessGetAssetJobsInfoRequest(MessageData<AssetJobsInfoRequest> messageData);
  271. //! A network request came in, Given a JOB ID (from the above Job Request), asking for the actual log for that job.
  272. AssetJobLogResponse ProcessGetAssetJobLogRequest(MessageData<AssetJobLogRequest> messageData);
  273. //! A network request came in asking for asset database location
  274. GetAbsoluteAssetDatabaseLocationResponse ProcessGetAbsoluteAssetDatabaseLocationRequest(MessageData<GetAbsoluteAssetDatabaseLocationRequest> messageData);
  275. //! This request comes in and is expected to do whatever heuristic is required in order to determine if an asset actually exists in the database.
  276. void OnRequestAssetExists(NetworkRequestID requestId, QString platform, QString searchTerm, AZ::Data::AssetId assetId);
  277. //! Searches the product and source asset tables to try and find a match
  278. QString GuessProductOrSourceAssetName(QString searchTerm, QString platform, bool useLikeSearch);
  279. void ProcessFilesToExamineQueue();
  280. void CheckForIdle();
  281. void CheckMissingFiles();
  282. void ProcessFingerprintClearRequest(AssetFingerprintClearRequest& request, AssetFingerprintClearResponse& response);
  283. void ProcessGetAssetJobsInfoRequest(AssetJobsInfoRequest& request, AssetJobsInfoResponse& response);
  284. void ProcessGetAssetJobLogRequest(const AssetJobLogRequest& request, AssetJobLogResponse& response);
  285. void ScheduleNextUpdate();
  286. void ProcessJobs();
  287. void RemoveEmptyFolders();
  288. void OnBuildersRegistered();
  289. void OnCatalogReady();
  290. void DelayedMetadataFileCheck();
  291. private:
  292. template <class R>
  293. bool Recv(unsigned int connId, QByteArray payload, R& request);
  294. void AssessFileInternal(QString fullFile, bool isDelete, bool fromScanner = false);
  295. void CheckSource(const FileEntry& source);
  296. void CheckMissingJobs(const SourceAssetReference& sourceAsset, const ScanFolderInfo* scanFolder, const AZStd::vector<JobDetails>& jobsThisTime);
  297. void CheckDeletedProductFile(QString normalizedPath);
  298. void CheckDeletedSourceFile(
  299. const SourceAssetReference& sourceAsset,
  300. AZStd::chrono::steady_clock::time_point initialProcessTime);
  301. void CheckModifiedSourceFile(const SourceAssetReference& sourceAsset, const ScanFolderInfo* scanFolderInfo);
  302. bool AnalyzeJob(JobDetails& details);
  303. void CheckDeletedCacheFolder(QString normalizedPath);
  304. void CheckDeletedSourceFolder(const SourceAssetReference& sourceAsset);
  305. void CheckCreatedSourceFolder(QString normalizedPath);
  306. void FailTopLevelSourceForIntermediate(const SourceAssetReference& intermediateAsset, AZStd::string_view errorMessage);
  307. void CheckMetaDataRealFiles(QString relativePath);
  308. bool DeleteProducts(const AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer& products);
  309. void DispatchFileChange();
  310. bool InitializeCacheRoot();
  311. void PopulateJobStateCache();
  312. void AutoFailJob(
  313. AZStd::string_view consoleMsg,
  314. AZStd::string_view autoFailReason,
  315. JobEntry jobEntry,
  316. AZStd::string_view jobLog = "");
  317. void AutoFailJob(
  318. AZStd::string_view consoleMsg,
  319. AZStd::string_view autoFailReason,
  320. const AZStd::vector<AssetProcessedEntry>::iterator& assetIter,
  321. AZ::s64 failureCauseSourceId = AzToolsFramework::AssetDatabase::InvalidEntryId,
  322. AZ::u32 failureCauseFingerprint = 0);
  323. using ProductInfoList = AZStd::vector<AZStd::pair<AzToolsFramework::AssetDatabase::ProductDatabaseEntry, const AssetBuilderSDK::JobProduct*>>;
  324. void WriteProductTableInfo(AZStd::pair<AzToolsFramework::AssetDatabase::ProductDatabaseEntry, const AssetBuilderSDK::JobProduct*>& pair, AZStd::vector<AZ::u32>& subIds, AZStd::unordered_set<AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry>& dependencyContainer, const AZStd::string& platform);
  325. //! given a full absolute path to a file, add any metadata files you find that apply.
  326. void AddMetadataFilesForFingerprinting(QString absolutePathToFileToCheck, SourceFilesForFingerprintingContainer& outFilesToFingerprint);
  327. // given a file name and a root to not go beyond, add the parent folder and its parent folders recursively
  328. // to the list of known folders.
  329. void AddKnownFoldersRecursivelyForFile(QString file, QString root);
  330. void CleanEmptyFolder(QString folder, QString root);
  331. void ProcessBuilders(const SourceAssetReference& sourceAsset, const ScanFolderInfo* scanFolder, const AssetProcessor::BuilderInfoList& builderInfoList);
  332. AZStd::vector<AZStd::string> GetExcludedFolders();
  333. struct SourceInfoWithFingerprints
  334. {
  335. SourceAssetReference m_sourceAssetReference;
  336. QString m_analysisFingerprint;
  337. };
  338. struct ConflictResult
  339. {
  340. enum class ConflictType
  341. {
  342. None,
  343. //! Indicates the conflict occurred because of a new intermediate overriding an existing source
  344. Intermediate,
  345. };
  346. ConflictType m_type;
  347. //! The file that has caused the conflict. If ConflictType == Intermediate, this is the source
  348. SourceAssetReference m_conflictingFile;
  349. };
  350. //! Search the database and the the source dependency maps for the the sourceUuid. if found returns the cached info
  351. bool SearchSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, SourceAssetReference& result);
  352. //! Adds the source to the database and returns the corresponding sourceDatabase Entry
  353. void AddSourceToDatabase(AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceDatabaseEntry, const SourceAssetReference& sourceAsset);
  354. // ! Get the engine, project and active gem root directories which could potentially be separate repositories.
  355. AZStd::vector<AZStd::string> GetPotentialRepositoryRoots();
  356. protected:
  357. // given a set of file info that definitely exist, warm the file cache up so
  358. // that we only query them once.
  359. void WarmUpFileCache(QSet<AssetFileInfo> filePaths);
  360. // Checks whether or not a file can be skipped for processing (ie, file content hasn't changed, builders haven't been added/removed, builders for the file haven't changed)
  361. bool CanSkipProcessingFile(const AssetFileInfo &fileInfo, AZ::u64& fileHash);
  362. void CheckReadyToAssessScanFiles();
  363. AZ::s64 GenerateNewJobRunKey();
  364. // Attempt to erase a log file. Failing to erase it is not a critical problem, but should be logged.
  365. // returns true if there is no log file there after this operation completes
  366. bool EraseLogFile(const char* fileName);
  367. // Load the old scan folders and match them up with new scan folders. Make sure they're
  368. bool MigrateScanFolders();
  369. //! Checks whether the AP is aware of any source file that has indicated the inputted
  370. //! source file as its dependency, and if found do we need to put that file back in the asset pipeline queue again
  371. QStringList GetSourceFilesWhichDependOnSourceFile(const QString& sourcePath, const ProductInfoList& updatedProducts);
  372. /** Given a BuilderSDK SourceFileDependency, try to find out what actual database source name is.
  373. * If it cannot be resolved but a UUID is available, the string result will contain the UUID (and we will return true).
  374. * If there's a problem that makes it unusable (such as no fields being filled in), the string will be blank
  375. * and this function will return false.
  376. */
  377. bool ResolveSourceFileDependencyPath(AssetBuilderSDK::SourceFileDependency& sourceDependency, QString& resultDatabaseSourceNames, QStringList& resolvedDependencyList);
  378. //! Updates the database with all the changes related to source dependency / job dependency:
  379. void UpdateSourceFileDependenciesDatabase(JobToProcessEntry& entry);
  380. //! Analyze JobDetail for every hold jobs
  381. void AnalyzeJobDetail(JobToProcessEntry& jobEntry);
  382. void UpdateJobDependency(JobDetails& jobDetails);
  383. void QueueIdleCheck();
  384. void UpdateWildcardDependencies(JobDetails& job, size_t jobDependencySlot, QStringList& resolvedDependencyList);
  385. //! Check whether the job can be analyzed by APM,
  386. //! A job cannot be analyzed if any of its dependent job hasn't been fingerprinted
  387. bool CanAnalyzeJob(const JobDetails& jobDetails);
  388. //! Analyzes and forward the job to the RCController if the job requires processing
  389. void ProcessJob(JobDetails& jobDetails);
  390. // Returns true if the path is inside the Cache and *not* inside the Intermediate Assets folder
  391. bool IsInCacheFolder(AZ::IO::PathView path) const;
  392. // Returns true if the path is inside the Intermediate Assets folder
  393. bool IsInIntermediateAssetsFolder(const SourceAssetReference& sourceAsset) const;
  394. bool IsInIntermediateAssetsFolder(AZ::IO::PathView path) const;
  395. bool IsInIntermediateAssetsFolder(QString path) const;
  396. // Checks if an intermediate product conflicts with an existing source asset
  397. // searchSourcePath should be the path of the intermediate to use to search for existing sources of the same name
  398. ConflictResult CheckIntermediateProductConflict(const char* searchSourcePath);
  399. bool CheckForIntermediateAssetLoop(const SourceAssetReference& sourceAsset, const SourceAssetReference& productAsset);
  400. void UpdateForCacheServer(JobDetails& jobDetails);
  401. //! Check whether the specified file is an LFS pointer file.
  402. bool IsLfsPointerFile(const AZStd::string& filePath);
  403. bool CheckMetadataIsAvailable(AZ::IO::PathView absolutePath);
  404. bool ShouldIgnorePendingMove(AZ::IO::PathView absolutePath, bool triggeredByMetadata, bool isDelete);
  405. bool ShouldDelayProcessingFile(const FileEntry& source, QString normalizedFilePath, bool triggeredByMetadata);
  406. //! Returns true if elapsed time is close enough to the delay process wait time
  407. //! This is not an exact check since QTimer is not precise with its event timing
  408. bool HasDelayProcessTimerElapsed(qint64 elapsedTime);
  409. AssetProcessor::PlatformConfiguration* m_platformConfig = nullptr;
  410. bool m_queuedExamination = false;
  411. bool m_hasProcessedCriticalAssets = false;
  412. QQueue<FileEntry> m_activeFiles;
  413. QSet<QString> m_alreadyActiveFiles; // a simple optimization to only do the exhaustive search if we know its there.
  414. AZStd::vector<AssetProcessedEntry> m_assetProcessedList;
  415. AZStd::shared_ptr<AssetDatabaseConnection> m_stateData;
  416. ThreadController<AssetCatalog>* m_assetCatalog;
  417. typedef QHash<QString, FileEntry> FileExamineContainer;
  418. FileExamineContainer m_filesToExamine; // order does not actually matter in this (yet)
  419. // Set of files which are metadata-enabled but don't have a metadata file.
  420. // These files will be delayed for processing for a short time to wait for a metadata file to show up.
  421. AZStd::unordered_map<AZ::IO::Path, QDateTime> m_delayProcessMetadataFiles;
  422. // Indicates if DelayedMetadataFileCheck has already been queued to run or not.
  423. bool m_delayProcessMetadataQueued = false;
  424. // Max delay time before creating a metadata file.
  425. // Avoid setting this too high as it will delay processing of new files.
  426. AZ::u32 m_metaCreationDelayMs = 0;
  427. // Set of files/folders that have been reported as pending for move. bool: false = old file path, true = new file path
  428. AZStd::unordered_map<AZ::IO::Path, bool> m_pendingMoves;
  429. AZStd::recursive_mutex m_pendingMovesMutex;
  430. // this map contains a list of source files that were discovered in the database before asset scanning began.
  431. // (so files from a previous run).
  432. // as asset scanning encounters files, it will remove them from this map, and when its done,
  433. // it will thus contain only the files that were in the database from last time, but were NOT found during file scan
  434. // in other words, files that have been deleted from disk since last run.
  435. // the key to this map is the absolute path of the file from last run, but with the current scan folder setup
  436. QMap<QString, SourceInfoWithFingerprints> m_sourceFilesInDatabase;
  437. // this map contains modtimes of all files AP processed last time it ran
  438. AZStd::unordered_map<AZStd::string, AZ::u64> m_fileModTimes;
  439. // this map contains hashes of all files AP processed last time it ran
  440. AZStd::unordered_map<AZStd::string, AZ::u64> m_fileHashes;
  441. QSet<QString> m_knownFolders; // a cache of all known folder names, normalized to have forward slashes.
  442. typedef AZStd::unordered_map<AZ::u64, AzToolsFramework::AssetSystem::JobInfo> JobRunKeyToJobInfoMap; // for when network requests come in about the jobInfo
  443. JobRunKeyToJobInfoMap m_jobRunKeyToJobInfoMap;
  444. AZStd::multimap<AZStd::string, AZ::u64> m_jobKeyToJobRunKeyMap;
  445. using SourceUUIDToSourceInfoMap = AZStd::unordered_map<AZ::Uuid, SourceAssetReference>;
  446. SourceUUIDToSourceInfoMap m_sourceUUIDToSourceInfoMap; // contains Source Asset UUID -> SourceAssetReference
  447. AZStd::mutex m_sourceUUIDToSourceInfoMapMutex;
  448. QString m_normalizedCacheRootPath;
  449. QDir m_cacheRootDir;
  450. bool m_isCurrentlyScanning = false;
  451. bool m_quitRequested = false;
  452. bool m_processedQueued = false;
  453. bool m_AssetProcessorIsBusy = true;
  454. bool m_catalogReady = false;
  455. bool m_buildersReady = false;
  456. bool m_alreadyScheduledUpdate = false;
  457. QMutex m_processingJobMutex;
  458. AZStd::unordered_set<AZStd::string> m_processingProductInfoList;
  459. AZ::s64 m_highestJobRunKeySoFar = 0;
  460. AZStd::vector<JobToProcessEntry> m_jobEntries;
  461. AZStd::unordered_set<JobDetails> m_jobsToProcess;
  462. //! This map is required to prevent multiple sourceFile modified events been send by the APM
  463. AZStd::unordered_map<AZ::Uuid, qint64> m_sourceFileModTimeMap;
  464. AZStd::unordered_map<JobIndentifier, AZ::u32> m_jobFingerprintMap;
  465. AZStd::unordered_map<JobDesc, AZStd::unordered_set<AZ::Uuid>> m_jobDescToBuilderUuidMap;
  466. AZStd::unique_ptr<PathDependencyManager> m_pathDependencyManager;
  467. AZStd::unique_ptr<SourceFileRelocator> m_sourceFileRelocator;
  468. AZStd::unique_ptr<LfsPointerFileValidator> m_lfsPointerFileValidator;
  469. JobDiagnosticTracker m_jobDiagnosticTracker{};
  470. QSet<QString> m_checkFoldersToRemove; //!< List of folders that needs to be checked for removal later by AP
  471. //! List of all scanfolders that are present in the database but not currently watched by AP
  472. AZStd::unordered_map<AZStd::string, AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry> m_scanFoldersInDatabase;
  473. int m_numOfJobsToAnalyze = 0;
  474. bool m_alreadyQueuedCheckForIdle = false;
  475. // Files from the scanner, waiting for initial analysis
  476. QSet<AssetFileInfo> m_scannerFiles;
  477. //////////////////// Analysis Early-Out feature ///////////////////
  478. // ComputeBuilderDirty builds the maps of which builders are dirty and how they have changed.
  479. // note that until ComputeBuilderDirty is called, it is assumed that *all* are dirty, to be conservative.
  480. // The data we actually care about for this feature:
  481. struct BuilderData
  482. {
  483. AZ::u8 m_flags = 0; // the flags from the builder registration
  484. AZ::Uuid m_fingerprint; // a hash of the fingerprint and version info
  485. bool m_isDirty = false;
  486. };
  487. void ComputeBuilderDirty();
  488. AZStd::string ComputeRecursiveDependenciesFingerprint(const SourceAssetReference& sourceAsset);
  489. AZStd::unordered_map<AZ::Uuid, BuilderData> m_builderDataCache;
  490. bool m_buildersAddedOrRemoved = true; //< true if any new builders exist. If this happens we actually need to re-analyze everything.
  491. bool m_anyBuilderChange = true;
  492. // Checks whether any of the builders specified have changed their fingerprint
  493. bool AreBuildersUnchanged(AZStd::string_view builderEntries, int& numBuildersEmittingSourceDependencies);
  494. /** Utility function: Given the input database row (from sources table), return an (ordered) set of all dependencies
  495. * including dependencies-of-dependencies. These will be absolute paths to the dependency file on disk.
  496. * Note that the output also includes the initial inputDatabasePath asset (but expanded to be absolute)
  497. * if a file does not exist, it will still in the list at the absolute path to where it may appear, so that
  498. * this result set can still use that for hashing.
  499. * if a source file is missing from disk, it will not be included in the result set, since this returns
  500. * full absolute paths.
  501. */
  502. void QueryAbsolutePathDependenciesRecursive(AZ::Uuid sourceUuid, SourceFilesForFingerprintingContainer& finalDependencyList, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::TypeOfDependency dependencyType);
  503. // we can't write a job to the database as not needing analysis the next time around,
  504. // until all jobs related to it are finished. This is becuase the jobs themselves are not written to the database
  505. // so until all jobs are finished, we need to re-analyze the source file next time.
  506. // (since if you terminate the asset processor while its still processing, we don't want it to skip over those
  507. // source files next time). So we keep a map of how many remaining outstanding jobs exist for a given
  508. // source file. Once the outstanding jobs hit zero we compute a final source fingerprint for analysis and save it.
  509. struct AnalysisTracker
  510. {
  511. int m_remainingJobsSpawned = 0;
  512. AZ::s64 m_databaseScanFolderId = -1;
  513. AZStd::string m_databaseSourceName;
  514. AZStd::set<AZ::Uuid> m_buildersInvolved; // this is intentionally a sorted set, since its used to generate a stable hash
  515. bool failedStatus = false; // if it fails, we avoid writing anything to the database, so that next time around, we reprocess the file.
  516. };
  517. // maps "absolute source path to file (normalized)" to tracking infomation struct above.
  518. using JobCounter = AZStd::unordered_map<AZStd::string, AnalysisTracker> ;
  519. JobCounter m_remainingJobsForEachSourceFile;
  520. // utility function: finds the source in the above map and updates it.
  521. enum class AnalysisTrackerUpdateType
  522. {
  523. JobFailed,
  524. JobStarted,
  525. JobFinished,
  526. };
  527. // ideally you would already have the absolute path to the file, and call this function with it:
  528. void UpdateAnalysisTrackerForFile(const SourceAssetReference& sourceAsset, AnalysisTrackerUpdateType updateType);
  529. // convenience overload of the above function when you have a jobEntry but no absolute path to the file.
  530. void UpdateAnalysisTrackerForFile(const JobEntry &entry, AnalysisTrackerUpdateType updateType);
  531. // Used to scan through products for anything that looks like a missing product dependency;
  532. MissingDependencyScanner m_missingDependencyScanner;
  533. // Metrics
  534. int m_numTotalSourcesFound = 0;
  535. int m_numSourcesNeedingFullAnalysis = 0;
  536. int m_numSourcesNotHandledByAnyBuilder = 0;
  537. bool m_reportedAnalysisMetrics = false;
  538. // cache these so we don't have to check them each time during analysis:
  539. QSet<QString> m_metaFilesWhichActuallyExistOnDisk;
  540. bool m_cachedMetaFilesExistMap = false;
  541. // when true, only processes files if their modtime or builder(s) have changed
  542. // defaults to true (in the settings) for GUI mode, false for batch mode
  543. bool m_allowModtimeSkippingFeature = false;
  544. // when true, startup scan is disabled which means modified files when asset processor
  545. // was not running won't be processed. this may be useful when working on pure code changes.
  546. bool m_initialScanSkippingFeature = false;
  547. // when true, a flag will be sent to builders process job indicating debug output/mode should be used
  548. bool m_builderDebugFlag = false;
  549. AZStd::unique_ptr<ExcludedFolderCache> m_excludedFolderCache{};
  550. // Cache of source -> list of dependencies for startup
  551. AZStd::unordered_map<AZ::Uuid, AZStd::vector<AzToolsFramework::AssetDatabase::PathOrUuid>> m_dependencyCache;
  552. // Cache is turned off after initial idle, it is not meant to handle invalidation or mixed dependency type queries.
  553. bool m_dependencyCacheEnabled = true;
  554. protected Q_SLOTS:
  555. void FinishAnalysis(SourceAssetReference sourceAsset);
  556. //////////////////////////////////////////////////////////
  557. };
  558. } // namespace AssetProcessor