Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/AutoReimport/AssetSourceFilenameCache.cpp
2025-05-18 13:04:45 +08:00

241 lines
7.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AutoReimport/AssetSourceFilenameCache.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetDataTagMap.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "Async/ParallelFor.h"
#include "CoreGlobals.h"
#include "EditorFramework/AssetImportData.h"
#include "HAL/Platform.h"
#include "HAL/PlatformCrt.h"
#include "Misc/PackageName.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
#include "Templates/UnrealTemplate.h"
#include "UObject/NameTypes.h"
#include "UObject/Object.h"
FAssetSourceFilenameCache::FAssetSourceFilenameCache()
: CachePipe(UE_SOURCE_LOCATION)
{
if (IsEngineExitRequested())
{
// This can get created for the first time on shutdown, if so don't do anything
return;
}
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
AssetRegistry.OnAssetsAdded().AddRaw(this, &FAssetSourceFilenameCache::HandleOnAssetsAdded);
AssetRegistry.OnAssetsRemoved().AddRaw(this, &FAssetSourceFilenameCache::HandleOnAssetsRemoved);
AssetRegistry.OnAssetRenamed().AddRaw(this, &FAssetSourceFilenameCache::HandleOnAssetRenamed);
UAssetImportData::OnImportDataChanged.AddRaw(this, &FAssetSourceFilenameCache::HandleOnAssetUpdated);
TArray<FAssetData> Assets;
if (AssetRegistry.GetAllAssets(Assets))
{
HandleOnAssetsAdded(Assets);
}
}
FAssetSourceFilenameCache& FAssetSourceFilenameCache::Get()
{
static FAssetSourceFilenameCache Cache;
return Cache;
}
void FAssetSourceFilenameCache::Shutdown()
{
FAssetRegistryModule* AssetRegistryModule = FModuleManager::GetModulePtr<FAssetRegistryModule>("AssetRegistry");
if (AssetRegistryModule)
{
IAssetRegistry* AssetRegistry = AssetRegistryModule->TryGet();
if (AssetRegistry)
{
AssetRegistry->OnAssetsAdded().RemoveAll(this);
AssetRegistry->OnAssetsRemoved().RemoveAll(this);
AssetRegistry->OnAssetRenamed().RemoveAll(this);
}
}
AssetRenamedEvent.Clear();
UAssetImportData::OnImportDataChanged.RemoveAll(this);
}
TOptional<FAssetImportInfo> FAssetSourceFilenameCache::ExtractAssetImportInfo(const FAssetData& AssetData)
{
static const FName LegacySourceFilePathName("SourceFile");
FAssetDataTagMapSharedView::FFindTagResult Result = AssetData.TagsAndValues.FindTag(UObject::SourceFileTagName());
if (Result.IsSet())
{
return FAssetImportInfo::FromJson(Result.GetValue());
}
else
{
FAssetDataTagMapSharedView::FFindTagResult ResultLegacy = AssetData.TagsAndValues.FindTag(LegacySourceFilePathName);
if (ResultLegacy.IsSet())
{
FAssetImportInfo Legacy;
Legacy.Insert(ResultLegacy.GetValue());
return Legacy;
}
else
{
return TOptional<FAssetImportInfo>();
}
}
}
void FAssetSourceFilenameCache::HandleOnAssetsAdded(TConstArrayView<FAssetData> Assets)
{
CachePipe.Launch(UE_SOURCE_LOCATION, [this, Assets=TArray<FAssetData>(Assets)](){
TRACE_CPUPROFILER_EVENT_SCOPE(FAssetSourceFilenameCache::HandleOnAssetsAdded);
const uint32 AssetCount = Assets.Num();
TArray<TOptional<FAssetImportInfo>> ImportDataList;
ImportDataList.SetNum(AssetCount);
ParallelFor(Assets.Num(), [&Assets, &ImportDataList](int32 Index)
{
ImportDataList[Index] = ExtractAssetImportInfo(Assets[Index]);
});
SourceFileToObjectPathCache.Reserve(SourceFileToObjectPathCache.Num() + Assets.Num());
for (uint32 Index = 0; Index < AssetCount; Index++)
{
const TOptional<FAssetImportInfo>& ImportData = ImportDataList[Index];
if (ImportData.IsSet())
{
for (const auto& SourceFile : ImportData->SourceFiles)
{
SourceFileToObjectPathCache.FindOrAdd(FPaths::GetCleanFilename(SourceFile.RelativeFilename)).Add(Assets[Index].GetSoftObjectPath());
}
}
}
}, LowLevelTasks::ETaskPriority::BackgroundNormal);
}
void FAssetSourceFilenameCache::HandleOnAssetsRemoved(TConstArrayView<FAssetData> Assets)
{
CachePipe.Launch(UE_SOURCE_LOCATION, [this, Assets=TArray<FAssetData>(Assets)](){
TRACE_CPUPROFILER_EVENT_SCOPE(FAssetSourceFilenameCache::HandleOnAssetsRemoved);
for (const FAssetData& AssetData : Assets)
{
TOptional<FAssetImportInfo> ImportData = ExtractAssetImportInfo(AssetData);
if (ImportData.IsSet())
{
for (auto& SourceFile : ImportData->SourceFiles)
{
FString CleanFilename = FPaths::GetCleanFilename(SourceFile.RelativeFilename);
if (auto* Objects = SourceFileToObjectPathCache.Find(CleanFilename))
{
Objects->Remove(AssetData.GetSoftObjectPath());
if (Objects->Num() == 0)
{
SourceFileToObjectPathCache.Remove(CleanFilename);
}
}
}
}
}
}, LowLevelTasks::ETaskPriority::BackgroundNormal);
}
void FAssetSourceFilenameCache::HandleOnAssetRenamed(const FAssetData& AssetData, const FString& OldPath)
{
// Capture by ref as we wait inline
CachePipe.Launch(UE_SOURCE_LOCATION, [this, &AssetData, &OldPath]() {
TOptional<FAssetImportInfo> ImportData = ExtractAssetImportInfo(AssetData);
if (ImportData.IsSet())
{
for (auto& SourceFile : ImportData->SourceFiles)
{
FString CleanFilename = FPaths::GetCleanFilename(SourceFile.RelativeFilename);
if (auto* Objects = SourceFileToObjectPathCache.Find(CleanFilename))
{
Objects->Remove(FSoftObjectPath(OldPath));
if (Objects->Num() == 0)
{
SourceFileToObjectPathCache.Remove(CleanFilename);
}
}
SourceFileToObjectPathCache.FindOrAdd(CleanFilename).Add(AssetData.GetSoftObjectPath());
}
}
}, LowLevelTasks::ETaskPriority::Normal, UE::Tasks::EExtendedTaskPriority::Inline).Wait();
AssetRenamedEvent.Broadcast(AssetData, OldPath);
}
void FAssetSourceFilenameCache::HandleOnAssetUpdated(const FAssetImportInfo& OldData, const UAssetImportData* ImportData)
{
// Run inline on pipe to safely use ImportData
CachePipe.Launch(UE_SOURCE_LOCATION, [this, &OldData, ImportData]() {
FSoftObjectPath ObjectPath = FSoftObjectPath(ImportData->GetOuter());
for (const auto& SourceFile : OldData.SourceFiles)
{
FString CleanFilename = FPaths::GetCleanFilename(SourceFile.RelativeFilename);
if (auto* Objects = SourceFileToObjectPathCache.Find(CleanFilename))
{
Objects->Remove(ObjectPath);
if (Objects->Num() == 0)
{
SourceFileToObjectPathCache.Remove(CleanFilename);
}
}
}
for (const auto& SourceFile : ImportData->SourceData.SourceFiles)
{
SourceFileToObjectPathCache.FindOrAdd(FPaths::GetCleanFilename(SourceFile.RelativeFilename)).Add(ObjectPath);
}
}, LowLevelTasks::ETaskPriority::Normal, UE::Tasks::EExtendedTaskPriority::Inline).Wait();
}
TArray<FAssetData> FAssetSourceFilenameCache::GetAssetsPertainingToFile(const IAssetRegistry& Registry, const FString& AbsoluteFilename) const
{
return CachePipe.Launch(UE_SOURCE_LOCATION, [this, &Registry, &AbsoluteFilename]()
{
TArray<FAssetData> Assets;
if (const auto* ObjectPaths = SourceFileToObjectPathCache.Find(FPaths::GetCleanFilename(AbsoluteFilename)))
{
for (const FSoftObjectPath& Path : *ObjectPaths)
{
FAssetData Asset = Registry.GetAssetByObjectPath(Path);
TOptional<FAssetImportInfo> ImportInfo = ExtractAssetImportInfo(Asset);
if (ImportInfo.IsSet())
{
auto AssetPackagePath = FPackageName::LongPackageNameToFilename(Asset.PackagePath.ToString() / TEXT(""));
// Attempt to find the matching source filename in the list of imported sorce files (generally there are only one of these)
const bool bWasImportedFromFile = ImportInfo->SourceFiles.ContainsByPredicate([&](const FAssetImportInfo::FSourceFile& File){
if (AbsoluteFilename == FPaths::ConvertRelativePathToFull(AssetPackagePath / File.RelativeFilename) ||
AbsoluteFilename == FPaths::ConvertRelativePathToFull(File.RelativeFilename))
{
return true;
}
return false;
});
if (bWasImportedFromFile)
{
Assets.Emplace();
Swap(Asset, Assets[Assets.Num() - 1]);
}
}
}
}
return Assets;
}, LowLevelTasks::ETaskPriority::Normal, UE::Tasks::EExtendedTaskPriority::Inline).GetResult();
}