Преглед изворни кода

Improved performance so having no uuid files does not slow down AP.

Updated UUID manager and asset catalog BuildRegistry to use the file state cache to avoid performance hit.  This required rearranging when the asset catalog is started up since it previously occurred before the file state cache was initialized.  File scanning now happens concurrently to AssetBuilder init.  When it is done, AP will record the result set and wait for builder init and BuildRegistry to finish before starting the analysis phase.  Completion of scanning will fill out the file state cache and then trigger the AssetBuilder init afterwards.

Also switched SourceAssetReference to use FixedMaxPath as profiling was showing a fair amount of time spent in allocations during normalization.

Signed-off-by: amzn-mike <[email protected]>
amzn-mike пре 2 година
родитељ
комит
f4f1612214

+ 108 - 85
Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp

@@ -462,123 +462,146 @@ namespace AssetProcessor
         m_catalogIsDirty = true;
         m_registryBuiltOnce = true;
 
-        AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
-        QMutexLocker locker(&m_registriesMutex);
-
-        for (QString platform : m_platforms)
         {
-            auto inserted = m_registries.insert(platform, AzFramework::AssetRegistry());
-            AzFramework::AssetRegistry& currentRegistry = inserted.value();
-            // list of source entries in the database that need to have their UUID updated
-            AZStd::vector<AzToolsFramework::AssetDatabase::SourceDatabaseEntry> sourceEntriesToUpdate;
+            AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
+            QMutexLocker locker(&m_registriesMutex);
 
-            QElapsedTimer timer;
-            timer.start();
-            auto databaseQueryCallback = [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined)
+            for (QString platform : m_platforms)
             {
-                SourceAssetReference sourceAsset(combined.m_scanFolderPK, combined.m_sourceName.c_str());
-                AZ::Data::AssetId assetId;
-                const bool fileExists = AZ::IO::FileIOBase::GetInstance()->Exists(sourceAsset.AbsolutePath().c_str());
+                auto inserted = m_registries.insert(platform, AzFramework::AssetRegistry());
+                AzFramework::AssetRegistry& currentRegistry = inserted.value();
+                // list of source entries in the database that need to have their UUID updated
+                AZStd::vector<AzToolsFramework::AssetDatabase::SourceDatabaseEntry> sourceEntriesToUpdate;
 
-                // Only try to update for files which actually exist
-                if (fileExists)
+                QElapsedTimer timer;
+                timer.start();
+                auto databaseQueryCallback = [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined)
                 {
-                    auto canonicalUuid = AssetUtilities::GetSourceUuid(sourceAsset);
-                    assetId = AZ::Data::AssetId(canonicalUuid, combined.m_subID);
+                    SourceAssetReference sourceAsset(combined.m_scanFolderPK, combined.m_scanFolder.c_str(), combined.m_sourceName.c_str());
+                    AZ::Data::AssetId assetId;
+
+                    auto* fileStateInterface = AZ::Interface<IFileStateRequests>::Get();
 
-                    if (canonicalUuid != combined.m_sourceGuid)
+                    if (!fileStateInterface)
                     {
-                        // Canonical UUID does not match stored UUID, this entry needs to be updated
-                        sourceEntriesToUpdate.emplace_back(
-                            combined.m_sourceID,
-                            combined.m_scanFolderID,
-                            combined.m_sourceName.c_str(),
-                            canonicalUuid, // Updated UUID
-                            combined.m_analysisFingerprint.c_str());
+                        AZ_Assert(false, "Programmer Error - IFileStateRequests interface is not available");
+                        return false;
                     }
-                }
-                else
-                {
-                    assetId = AZ::Data::AssetId(combined.m_sourceGuid, combined.m_subID);
-                }
 
-                // relative file path is gotten by removing the platform and game from the product name
-                AZStd::string_view relativeProductPath = AssetUtilities::StripAssetPlatformNoCopy(combined.m_productName);
-                QString fullProductPath = m_cacheRoot.absoluteFilePath(combined.m_productName.c_str());
+                    FileStateInfo fileInfo;
+                    const bool fileExists = fileStateInterface->GetFileInfo(sourceAsset.AbsolutePath().c_str(), &fileInfo);
 
-                AZ::Data::AssetInfo info;
-                info.m_assetType = combined.m_assetType;
-                info.m_relativePath = relativeProductPath;
-                info.m_assetId = assetId;
-                info.m_sizeBytes = AZ::IO::SystemFile::Length(fullProductPath.toUtf8().constData());
+                    // Only try to update for files which actually exist
+                    if (fileExists)
+                    {
+                        auto canonicalUuid = AssetUtilities::GetSourceUuid(sourceAsset);
+                        assetId = AZ::Data::AssetId(canonicalUuid, combined.m_subID);
+
+                        if (canonicalUuid != combined.m_sourceGuid)
+                        {
+                            // Canonical UUID does not match stored UUID, this entry needs to be updated
+                            sourceEntriesToUpdate.emplace_back(
+                                combined.m_sourceID,
+                                combined.m_scanFolderID,
+                                combined.m_sourceName.c_str(),
+                                canonicalUuid, // Updated UUID
+                                combined.m_analysisFingerprint.c_str());
+                        }
+                    }
+                    else
+                    {
+                        assetId = AZ::Data::AssetId(combined.m_sourceGuid, combined.m_subID);
+                    }
 
-                // also register it at the legacy id(s) if its different:
-                AZ::Data::AssetId legacyAssetId(combined.m_legacyGuid, 0);
-                currentRegistry.RegisterAsset(assetId, info);
+                    // relative file path is gotten by removing the platform and game from the product name
+                    AZStd::string_view relativeProductPath = AssetUtilities::StripAssetPlatformNoCopy(combined.m_productName);
+                    QString fullProductPath = m_cacheRoot.absoluteFilePath(combined.m_productName.c_str());
 
-                if (legacyAssetId != assetId)
-                {
-                    currentRegistry.RegisterLegacyAssetMapping(legacyAssetId, assetId);
-                }
+                    AZ::Data::AssetInfo info;
+                    info.m_assetType = combined.m_assetType;
+                    info.m_relativePath = relativeProductPath;
+                    info.m_assetId = assetId;
+                    info.m_sizeBytes = fileInfo.m_fileSize;
 
-                AZStd::unordered_set<AZ::Data::AssetId> legacySourceAssetIds;
+                    // also register it at the legacy id(s) if its different:
+                    AZ::Data::AssetId legacyAssetId(combined.m_legacyGuid, 0);
+                    currentRegistry.RegisterAsset(assetId, info);
 
-                if (fileExists)
-                {
-                    auto legacySourceUuids = AssetUtilities::GetLegacySourceUuids(sourceAsset);
-                    legacySourceAssetIds.reserve(legacySourceUuids.size());
+                    if (legacyAssetId != assetId)
+                    {
+                        currentRegistry.RegisterLegacyAssetMapping(legacyAssetId, assetId);
+                    }
+
+                    AZStd::unordered_set<AZ::Data::AssetId> legacySourceAssetIds;
 
-                    for (const auto& legacyUuid : legacySourceUuids)
+                    if (fileExists)
                     {
-                        AZ::Data::AssetId legacySourceAssetId(legacyUuid, combined.m_subID);
+                        auto legacySourceUuids = AssetUtilities::GetLegacySourceUuids(sourceAsset);
+                        legacySourceAssetIds.reserve(legacySourceUuids.size());
 
-                        if (legacySourceAssetId != assetId)
+                        for (const auto& legacyUuid : legacySourceUuids)
                         {
-                            legacySourceAssetIds.emplace(legacySourceAssetId);
-                            currentRegistry.RegisterLegacyAssetMapping(legacySourceAssetId, assetId);
+                            AZ::Data::AssetId legacySourceAssetId(legacyUuid, combined.m_subID);
+
+                            if (legacySourceAssetId != assetId)
+                            {
+                                legacySourceAssetIds.emplace(legacySourceAssetId);
+                                currentRegistry.RegisterLegacyAssetMapping(legacySourceAssetId, assetId);
+                            }
                         }
                     }
-                }
 
-                // now include the additional legacies based on the SubIDs by which this asset was previously referred to.
-                for (const auto& entry : combined.m_legacySubIDs)
-                {
-                    AZ::Data::AssetId legacySubID(combined.m_sourceGuid, entry.m_subID);
-                    if ((legacySubID != assetId) && (legacySubID != legacyAssetId) && !legacySourceAssetIds.contains(legacySubID))
+                    // now include the additional legacies based on the SubIDs by which this asset was previously referred to.
+                    for (const auto& entry : combined.m_legacySubIDs)
                     {
-                        currentRegistry.RegisterLegacyAssetMapping(legacySubID, assetId);
+                        AZ::Data::AssetId legacySubID(combined.m_sourceGuid, entry.m_subID);
+                        if ((legacySubID != assetId) && (legacySubID != legacyAssetId) && !legacySourceAssetIds.contains(legacySubID))
+                        {
+                            currentRegistry.RegisterLegacyAssetMapping(legacySubID, assetId);
+                        }
                     }
 
-                }
+                    return true; // see them all
+                };
 
-                return true;//see them all
-            };
+                m_db->QueryCombined(
+                    databaseQueryCallback,
+                    AZ::Uuid::CreateNull(),
+                    nullptr,
+                    platform.toUtf8().constData(),
+                    AzToolsFramework::AssetSystem::JobStatus::Any,
+                    true); /*we still need legacy IDs - hardly anyone else does*/
 
-            m_db->QueryCombined(
-                databaseQueryCallback, AZ::Uuid::CreateNull(),
-                nullptr,
-                platform.toUtf8().constData(),
-                AzToolsFramework::AssetSystem::JobStatus::Any,
-                true); /*we still need legacy IDs - hardly anyone else does*/
+                m_db->QueryProductDependenciesTable(
+                    [this, &platform](AZ::Data::AssetId& assetId, AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)
+                    {
+                        if (AzFramework::StringFunc::Equal(entry.m_platform.c_str(), platform.toUtf8().data()))
+                        {
+                            m_registries[platform].RegisterAssetDependency(
+                                assetId,
+                                AZ::Data::ProductDependency{ AZ::Data::AssetId(entry.m_dependencySourceGuid, entry.m_dependencySubID),
+                                                             entry.m_dependencyFlags });
+                        }
 
-            m_db->QueryProductDependenciesTable([this, &platform](AZ::Data::AssetId& assetId, AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)
-            {
-                if (AzFramework::StringFunc::Equal(entry.m_platform.c_str(), platform.toUtf8().data()))
+                        return true;
+                    });
+
+                // Update any old source UUIDs
+                for (auto& sourceDatabaseEntry : sourceEntriesToUpdate)
                 {
-                    m_registries[platform].RegisterAssetDependency(assetId, AZ::Data::ProductDependency{ AZ::Data::AssetId(entry.m_dependencySourceGuid, entry.m_dependencySubID), entry.m_dependencyFlags });
+                    m_db->SetSource(sourceDatabaseEntry);
                 }
 
-                return true;
-            });
-
-            // Update any old source UUIDs
-            for (auto& sourceDatabaseEntry : sourceEntriesToUpdate)
-            {
-                m_db->SetSource(sourceDatabaseEntry);
+                AZ_TracePrintf(
+                    "Catalog",
+                    "Read %u assets from database for %s in %fs\n",
+                    currentRegistry.m_assetIdToInfo.size(),
+                    platform.toUtf8().constData(),
+                    timer.elapsed() / 1000.0f);
             }
-
-            AZ_TracePrintf("Catalog", "Read %u assets from database for %s in %fs\n", currentRegistry.m_assetIdToInfo.size(), platform.toUtf8().constData(), timer.elapsed() / 1000.0f);
         }
+
+        Q_EMIT CatalogLoaded();
     }
 
     void AssetCatalog::OnDependencyResolved(const AZ::Data::AssetId& assetId, const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry)

+ 1 - 0
Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h

@@ -63,6 +63,7 @@ namespace AssetProcessor
         // outgoing message to the network
         void SendAssetMessage(AzFramework::AssetSystem::AssetNotificationMessage message);
         void AsyncAssetCatalogStatusResponse(AssetCatalogStatus status);
+        void CatalogLoaded();
 
     public Q_SLOTS:
         // incoming message from the AP

+ 3 - 3
Code/Tools/AssetProcessor/native/AssetManager/SourceAssetReference.cpp

@@ -150,17 +150,17 @@ namespace AssetProcessor
         return !m_absolutePath.empty();
     }
 
-    AZ::IO::Path SourceAssetReference::AbsolutePath() const
+    AZ::IO::FixedMaxPath SourceAssetReference::AbsolutePath() const
     {
         return m_absolutePath;
     }
 
-    AZ::IO::Path SourceAssetReference::RelativePath() const
+    AZ::IO::FixedMaxPath SourceAssetReference::RelativePath() const
     {
         return m_relativePath;
     }
 
-    AZ::IO::Path SourceAssetReference::ScanFolderPath() const
+    AZ::IO::FixedMaxPath SourceAssetReference::ScanFolderPath() const
     {
         return m_scanFolderPath;
     }

+ 6 - 6
Code/Tools/AssetProcessor/native/AssetManager/SourceAssetReference.h

@@ -60,17 +60,17 @@ namespace AssetProcessor
         explicit operator bool() const;
 
         bool IsValid() const;
-        AZ::IO::Path AbsolutePath() const;
-        AZ::IO::Path RelativePath() const;
-        AZ::IO::Path ScanFolderPath() const;
+        AZ::IO::FixedMaxPath AbsolutePath() const;
+        AZ::IO::FixedMaxPath RelativePath() const;
+        AZ::IO::FixedMaxPath ScanFolderPath() const;
         AZ::s64 ScanFolderId() const;
 
     private:
         void Normalize();
 
-        AZ::IO::Path m_absolutePath;
-        AZ::IO::Path m_relativePath;
-        AZ::IO::Path m_scanFolderPath;
+        AZ::IO::FixedMaxPath m_absolutePath;
+        AZ::IO::FixedMaxPath m_relativePath;
+        AZ::IO::FixedMaxPath m_scanFolderPath;
         AZ::s64 m_scanFolderId{};
     };
 }

+ 34 - 9
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp

@@ -194,11 +194,6 @@ namespace AssetProcessor
                  (status == AssetProcessor::AssetScanningStatus::Stopped))
         {
             AssetProcessor::StatsCapture::EndCaptureStat("AssetScanning");
-            // place a message in the queue that will cause us to transition
-            // into a "no longer scanning" state and then continue with the next phase
-            // we place this at the end of the queue rather than calling it immediately, becuase
-            // other messages may still be in the queue such as the incoming file list.
-            QMetaObject::invokeMethod(this, "FinishAssetScan", Qt::QueuedConnection);
         }
     }
 
@@ -3206,6 +3201,15 @@ namespace AssetProcessor
         }
     }
 
+    void AssetProcessorManager::CheckReadyToAssessScanFiles()
+    {
+        if (m_catalogReady && m_buildersReady && m_scannerFiles.size() > 0)
+        {
+            AssessFilesFromScanner(m_scannerFiles);
+            m_scannerFiles = {};
+        }
+    }
+
     // The file cache is used before actually hitting physical media to determine the
     // existence of files and to retrieve the file's hash.
     // It assumes that the presence of a file in the cache means the file exists.
@@ -3329,6 +3333,19 @@ namespace AssetProcessor
         }
 
         AssetProcessor::StatsCapture::EndCaptureStat("InitialFileAssessment");
+
+        // place a message in the queue that will cause us to transition
+        // into a "no longer scanning" state and then continue with the next phase
+        // we place this at the end of the queue rather than calling it immediately, becuase
+        // other messages may still be in the queue such as the incoming file list.
+        QMetaObject::invokeMethod(this, "FinishAssetScan", Qt::QueuedConnection);
+    }
+
+    void AssetProcessorManager::RecordFilesFromScanner(QSet<AssetFileInfo> filePaths)
+    {
+        m_scannerFiles = filePaths;
+
+        CheckReadyToAssessScanFiles();
     }
 
     void AssetProcessorManager::RecordFoldersFromScanner(QSet<AssetFileInfo> folderPaths)
@@ -3892,7 +3909,7 @@ namespace AssetProcessor
 
             AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms = scanFolder->GetPlatforms();
 
-            const AssetBuilderSDK::CreateJobsRequest createJobsRequest(builderInfo.m_busId, sourceAsset.RelativePath().Native(), scanFolder->ScanPath().toUtf8().constData(), platforms, sourceUUID);
+            const AssetBuilderSDK::CreateJobsRequest createJobsRequest(builderInfo.m_busId, sourceAsset.RelativePath().c_str(), scanFolder->ScanPath().toUtf8().constData(), platforms, sourceUUID);
 
             AssetBuilderSDK::CreateJobsResponse createJobsResponse;
 
@@ -4785,6 +4802,17 @@ namespace AssetProcessor
     void AssetProcessorManager::OnBuildersRegistered()
     {
         ComputeBuilderDirty();
+
+        m_buildersReady = true;
+
+        CheckReadyToAssessScanFiles();
+    }
+
+    void AssetProcessorManager::OnCatalogReady()
+    {
+        m_catalogReady = true;
+
+        CheckReadyToAssessScanFiles();
     }
 
     void AssetProcessorManager::ComputeBuilderDirty()
@@ -5565,7 +5593,4 @@ namespace AssetProcessor
         }
         return filesFound;
     }
-
-
 } // namespace AssetProcessor
-

+ 9 - 0
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h

@@ -287,6 +287,7 @@ namespace AssetProcessor
         void AssetCancelled(JobEntry jobEntry);
 
         void AssessFilesFromScanner(QSet<AssetFileInfo> filePaths);
+        void RecordFilesFromScanner(QSet<AssetFileInfo> filePaths);
         void RecordFoldersFromScanner(QSet<AssetFileInfo> folderPaths);
 
         virtual void AssessModifiedFile(QString filePath);
@@ -325,6 +326,7 @@ namespace AssetProcessor
         void RemoveEmptyFolders();
 
         void OnBuildersRegistered();
+        void OnCatalogReady();
 
     private:
         template <class R>
@@ -406,6 +408,8 @@ namespace AssetProcessor
         // 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)
         bool CanSkipProcessingFile(const AssetFileInfo &fileInfo, AZ::u64& fileHash);
 
+        void CheckReadyToAssessScanFiles();
+
         AZ::s64 GenerateNewJobRunKey();
         // Attempt to erase a log file.  Failing to erase it is not a critical problem, but should be logged.
         // returns true if there is no log file there after this operation completes
@@ -503,6 +507,8 @@ namespace AssetProcessor
         bool m_quitRequested = false;
         bool m_processedQueued = false;
         bool m_AssetProcessorIsBusy = true;
+        bool m_catalogReady = false;
+        bool m_buildersReady = false;
 
         bool m_alreadyScheduledUpdate = false;
         QMutex m_processingJobMutex;
@@ -528,6 +534,9 @@ namespace AssetProcessor
         int m_numOfJobsToAnalyze = 0;
         bool m_alreadyQueuedCheckForIdle = false;
 
+        // Files from the scanner, waiting for initial analysis
+        QSet<AssetFileInfo> m_scannerFiles;
+
         //////////////////// Analysis Early-Out feature ///////////////////
         // ComputeBuilderDirty builds the maps of which builders are dirty and how they have changed.
         // note that until ComputeBuilderDirty is called, it is assumed that *all* are dirty, to be conservative.

+ 1 - 1
Code/Tools/AssetProcessor/native/assetprocessor.h

@@ -270,7 +270,7 @@ namespace AssetProcessor
 
         AZStd::string ToString() const
         {
-            AZStd::string lowerSourceName = m_sourceAsset.AbsolutePath().Native();
+            AZStd::string lowerSourceName = m_sourceAsset.AbsolutePath().c_str();
             AZStd::to_lower(lowerSourceName.begin(), lowerSourceName.end());
 
             return AZStd::string::format("%s %s %s", lowerSourceName.c_str(), m_platformIdentifier.c_str(), m_jobKey.c_str());

+ 35 - 16
Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp

@@ -310,6 +310,12 @@ void ApplicationManagerBase::Rescan()
 void ApplicationManagerBase::InitAssetCatalog()
 {
     using namespace AssetProcessor;
+
+    if (m_assetCatalog)
+    {
+        return;
+    }
+
     ThreadController<AssetCatalog>* assetCatalogHelper = new ThreadController<AssetCatalog>();
 
     addRunningThread(assetCatalogHelper);
@@ -322,6 +328,17 @@ void ApplicationManagerBase::InitAssetCatalog()
                 connect(m_assetProcessorManager, &AssetProcessorManager::SourceQueued, catalog, &AssetCatalog::OnSourceQueued);
                 connect(m_assetProcessorManager, &AssetProcessorManager::SourceFinished, catalog, &AssetCatalog::OnSourceFinished);
                 connect(m_assetProcessorManager, &AssetProcessorManager::PathDependencyResolved, catalog, &AssetCatalog::OnDependencyResolved);
+                connect(
+                    catalog,
+                    &AssetCatalog::SendAssetMessage,
+                    this,
+                    [](auto message)
+                    {
+                        AssetProcessor::ConnectionBus::Broadcast(&AssetProcessor::ConnectionBus::Events::SendPerPlatform, 0, message, QString::fromUtf8(message.m_platform.c_str()));
+                    },
+                    Qt::QueuedConnection);
+                connect(m_connectionManager, &ConnectionManager::ConnectionReady, catalog, &AssetCatalog::OnConnect, Qt::QueuedConnection);
+                connect(catalog, &AssetCatalog::CatalogLoaded, m_assetProcessorManager, &AssetProcessorManager::OnCatalogReady);
 
                 return catalog;
             });
@@ -359,9 +376,20 @@ void ApplicationManagerBase::InitAssetScanner()
     m_assetScanner = new AssetScanner(m_platformConfiguration);
 
     // asset processor manager
+    QObject::connect(
+        m_assetScanner,
+        &AssetScanner::AssetScanningStatusChanged,
+        m_assetProcessorManager,
+        [this](auto status)
+        {
+            if (status == AssetProcessor::AssetScanningStatus::Completed)
+            {
+                InitAssetCatalog();
+            }
+        });
     QObject::connect(m_assetScanner, &AssetScanner::AssetScanningStatusChanged, m_assetProcessorManager, &AssetProcessorManager::OnAssetScannerStatusChange);
-    QObject::connect(m_assetScanner, &AssetScanner::FilesFound,                 m_assetProcessorManager, &AssetProcessorManager::AssessFilesFromScanner);
-    QObject::connect(m_assetScanner, &AssetScanner::FoldersFound,                 m_assetProcessorManager, &AssetProcessorManager::RecordFoldersFromScanner);
+    QObject::connect(m_assetScanner, &AssetScanner::FilesFound,                 m_assetProcessorManager, &AssetProcessorManager::RecordFilesFromScanner);
+    QObject::connect(m_assetScanner, &AssetScanner::FoldersFound,               m_assetProcessorManager, &AssetProcessorManager::RecordFoldersFromScanner);
 
     QObject::connect(m_assetScanner, &AssetScanner::FilesFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
     QObject::connect(m_assetScanner, &AssetScanner::FoldersFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
@@ -447,7 +475,7 @@ void ApplicationManagerBase::InitFileMonitor(AZStd::unique_ptr<FileWatcherBase>
         const ScanFolderInfo& info = m_platformConfiguration->GetScanFolderAt(folderIdx);
         m_fileWatcher->AddFolderWatch(info.ScanPath(), info.RecurseSubFolders());
     }
-  
+
     const auto OnFileAdded = [this](QString path)
     {
         m_fileStateCache->AddFile(path);
@@ -534,19 +562,13 @@ void ApplicationManagerBase::InitConnectionManager()
 
     m_connectionManager = new ConnectionManager();
 
-    QObject* connectionAndChangeMessagesThreadContext = this;
-
     // AssetProcessor Manager related stuff
     auto forwardMessageFunction = [](AzFramework::AssetSystem::AssetNotificationMessage message)
         {
             EBUS_EVENT(AssetProcessor::ConnectionBus, SendPerPlatform, 0, message, QString::fromUtf8(message.m_platform.c_str()));
         };
 
-    [[maybe_unused]] bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection);
-    AZ_Assert(result, "Failed to connect to AssetCatalog signal");
-
-    result = QObject::connect(m_connectionManager, &ConnectionManager::ConnectionReady, GetAssetCatalog(), &AssetProcessor::AssetCatalog::OnConnect, Qt::QueuedConnection);
-    AZ_Assert(result, "Failed to connect to AssetCatalog signal");
+    [[maybe_unused]] bool result;
 
     //Application manager related stuff
 
@@ -1448,7 +1470,6 @@ bool ApplicationManagerBase::Activate()
     InitFileProcessor();
 
     InitUuidManager();
-    InitAssetCatalog();
     InitFileMonitor(AZStd::make_unique<FileWatcher>());
     InitAssetScanner();
     InitAssetServerHandler();
@@ -1539,6 +1560,9 @@ bool ApplicationManagerBase::Activate()
 
     builderRegistrationThread.detach();
 
+    // While waiting for builder registration, start scanning
+    GetAssetScanner()->StartScan();
+
     return true;
 }
 
@@ -1559,11 +1583,6 @@ bool ApplicationManagerBase::PostActivate()
             AZ::SystemTickBus::Broadcast(&AZ::SystemTickEvents::OnSystemTick);
         });
 
-    // now that everything is up and running, we start scanning.  Before this, we don't want file events to start percolating through the
-    // asset system.
-
-    GetAssetScanner()->StartScan();
-
     return true;
 }
 

+ 21 - 6
Code/Tools/AssetProcessor/native/utilities/UuidManager.cpp

@@ -11,6 +11,7 @@
 #include <native/utilities/UuidManager.h>
 #include <native/utilities/assetUtils.h>
 #include <Metadata/MetadataManager.h>
+#include <native/AssetManager/FileStateCache.h>
 
 namespace AssetProcessor
 {
@@ -112,17 +113,31 @@ namespace AssetProcessor
             return itr->second;
         }
 
-        UuidEntry uuidInfo;
+        auto* fileStateInterface = AZ::Interface<IFileStateRequests>::Get();
 
-        // Check if there's a metadata file that already contains a saved UUID
-        if (GetMetadataManager()->GetValue(sourceAsset.AbsolutePath(), UuidKey, uuidInfo))
+        if (!fileStateInterface)
         {
-            m_uuids[normalizedPath] = uuidInfo;
+            AZ_Assert(false, "Programmer Error - IFileStateRequests interface is not available");
+            return {};
+        }
+
+        const bool fileExists = fileStateInterface->Exists(AzToolsFramework::MetadataManager::ToMetadataPath(sourceAsset.AbsolutePath().c_str()).c_str());
+
+        // Metadata manager can't use the file state cache since it is in AzToolsFramework, so it's faster to do an Exists check up-front.
+        if (fileExists)
+        {
+            UuidEntry uuidInfo;
+
+            // Check if there's a metadata file that already contains a saved UUID
+            if (GetMetadataManager()->GetValue(sourceAsset.AbsolutePath(), UuidKey, uuidInfo))
+            {
+                m_uuids[normalizedPath] = uuidInfo;
 
-            return uuidInfo;
+                return uuidInfo;
+            }
         }
 
-        if (!AZ::IO::FileIOBase::GetInstance()->Exists(sourceAsset.AbsolutePath().c_str()))
+        if (!fileStateInterface->Exists(sourceAsset.AbsolutePath().c_str()))
         {
             AZ_Error(
                 "UuidManager",