Files
UnrealEngine/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistryImpl.h
2025-05-18 13:04:45 +08:00

1120 lines
50 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AssetDataGatherer.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryState.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "AssetRegistry/PathTree.h"
#include "Containers/RingBuffer.h"
#include "Containers/Map.h"
#include "Containers/Set.h"
#include "HAL/CriticalSection.h"
#include "Math/NumericLimits.h"
#include "Misc/Optional.h"
#include "Misc/TransactionallySafeRWLock.h"
#include "Misc/ScopeRWLock.h"
#include "ModuleDescriptor.h"
#include "PackageDependencyData.h"
#include "Templates/Function.h"
#include "Templates/UniquePtr.h"
#include <atomic>
// Disable premade asset registry until the target specifies it in UBT
#ifdef ASSETREGISTRY_ENABLE_PREMADE_REGISTRY_IN_EDITOR
#error "Please use ASSETREGISTRY_FORCE_PREMADE_REGISTRY_IN_EDITOR instead of ASSETREGISTRY_ENABLE_PREMADE_REGISTRY_IN_EDITOR"
#endif
#ifndef ASSETREGISTRY_FORCE_PREMADE_REGISTRY_IN_EDITOR
#define ASSETREGISTRY_FORCE_PREMADE_REGISTRY_IN_EDITOR 0
#endif
class ITargetPlatform;
class FDependsNode;
struct FARFilter;
class FAssetDataGatherer;
struct FFileChangeData;
class FPackageReader;
class UAssetRegistryImpl;
namespace UE::AssetRegistry::Impl { struct FEventContext; }
namespace UE::AssetRegistry::Premade { enum class ELoadResult : uint8; }
namespace UE::AssetRegistry::Premade { struct FAsyncConsumer; }
namespace UE::AssetRegistry::Impl { struct FInitializeContext; }
namespace UE::AssetRegistry::Impl { struct FScanPathContext; }
namespace UE::AssetRegistry::Impl { struct FTickContext; }
namespace UE::AssetRegistry::Impl { struct FClassInheritanceContext; }
namespace UE::AssetRegistry::Private { class FRWLockWithPriority; }
namespace UE::AssetRegistry::Private { using FInterfaceRWLock = FRWLockWithPriority; }
namespace UE::AssetRegistry { template <typename TScopeLockType> class TRWScopeLockWithPriority; }
namespace UE::AssetRegistry { using FInterfaceReadScopeLock = TRWScopeLockWithPriority<UE::TReadScopeLock<Private::FRWLockWithPriority>>; }
namespace UE::AssetRegistry { using FInterfaceWriteScopeLock = TRWScopeLockWithPriority<UE::TWriteScopeLock<Private::FRWLockWithPriority>>; }
namespace UE::AssetRegistry { using FInterfaceRWScopeLock = class FRWScopeLockWithPriority; }
#if WITH_EDITOR
namespace UE::AssetDependencyGatherer::Private { class FRegisteredAssetDependencyGatherer; }
#endif
namespace UE::AssetRegistry
{
namespace Premade
{
/**
* A struct to consume a Premade asset registry on an async thread. It supports a cheap Wait() call so that it can be Waited on
* by frequent AssetRegistry interface calls for the rest of the process.
*/
struct FAsyncConsumer
{
~FAsyncConsumer();
/** Sets the Consumer into need-to-wait mode; Wait will block until Consume is called. */
void PrepareForConsume();
/**
* Does not return until after the Premade AssetRegistryState has been added into the target AssetRegistry, or it has
* been decided not to be used. For performance reasons, this must be called within the WriteScopeLock, and the caller
* must handle the possibility that it leaves and reenters the lock.
*/
void Wait(UAssetRegistryImpl& UARI, FInterfaceWriteScopeLock& ScopeLock);
/** Callback from the async thread to consume the Premade ARState */
void Consume(UAssetRegistryImpl& UARI, UE::AssetRegistry::Impl::FEventContext& EventContext, ELoadResult LoadResult, FAssetRegistryState&& ARState);
private:
/**
* ReferenceCounter for the Consumed event. Also used to decide whether Waiting is necessary. Read/Write only inside the lock.
*/
int32 ReferenceCount = 0;
/** Event used to Wait for Consume. Allocated/deallocated within the lock. Can be waited on outside the lock if RefCount is held. */
FEvent* Consumed = nullptr;
};
} // namespace Premade within UE::AssetRegistry
namespace Private
{
/** Custom lock class that can prioritize waiters upon request. This is used to allow threads with critical requests
* to request that the gatherer thread pause its work and allow the higher pri threads to jump in. Note that for this
* to work correctly it must be used with the associated TPriorityScopeLock types
* (TScopeLockWithPriority and FRWScopeLockWithPriority)
*/
class FRWLockWithPriority
{
public:
FORCEINLINE void WriteLock()
{
Mutex.WriteLock();
}
FORCEINLINE void WriteUnlock()
{
Mutex.WriteUnlock();
}
FORCEINLINE void ReadLock()
{
Mutex.ReadLock();
}
FORCEINLINE void ReadUnlock()
{
Mutex.ReadUnlock();
}
bool HasWaiters() const
{
bool Result;
UE_AUTORTFM_OPEN
{
Result = HighPriorityWaitersCount.load(std::memory_order_relaxed) > 0;
};
return Result;
}
private:
friend FInterfaceReadScopeLock;
friend FInterfaceWriteScopeLock;
friend FInterfaceRWScopeLock;
FTransactionallySafeRWLock Mutex;
std::atomic<int32> HighPriorityWaitersCount;
};
enum ELockPriority : uint8
{
PriorityLow,
PriorityHigh
};
} // namespace Private within namespace UE::AssetRegistry
/**
* Tracks high-priority waiters on a lock, in an thread- and transactionally-safe manner.
*/
class FScopedHighPriorityWaitTracker
{
public:
FScopedHighPriorityWaitTracker(std::atomic<int32>* InCounter, Private::ELockPriority InPriority)
: Counter((InPriority == Private::PriorityHigh) ? InCounter : nullptr)
{
if (Counter)
{
UE_AUTORTFM_OPEN
{
Counter->fetch_add(1, std::memory_order_relaxed);
};
AutoRTFM::PushOnAbortHandler(this, [this]
{
Counter->fetch_sub(1, std::memory_order_relaxed);
});
}
}
~FScopedHighPriorityWaitTracker()
{
if (Counter)
{
UE_AUTORTFM_OPEN
{
Counter->fetch_sub(1, std::memory_order_relaxed);
};
AutoRTFM::PopOnAbortHandler(this);
}
}
private:
std::atomic<int32>* Counter = nullptr;
};
/** Keeps a FRWLock read-locked while this scope lives */
/** This is almost a clone of the existing FReadScopeLock and similar types
however this adds an extra flag to help the background processing thread
know when a higher priority thread would like to gain access to the protected
data */
template <typename TScopeLockType> class TRWScopeLockWithPriority
{
public:
UE_NODISCARD_CTOR explicit TRWScopeLockWithPriority(Private::FRWLockWithPriority& InLock,
Private::ELockPriority InPriority = Private::PriorityHigh)
: Lock(InLock)
, Priority(InPriority)
{
FScopedHighPriorityWaitTracker Tracker(&Lock.HighPriorityWaitersCount, Priority);
GuardWrapper.Emplace(Lock);
}
TOptional<TScopeLockType> GuardWrapper;
UE::AssetRegistry::Private::FRWLockWithPriority& Lock;
UE::AssetRegistry::Private::ELockPriority Priority;
};
class FRWScopeLockWithPriority
{
public:
UE_NODISCARD_CTOR explicit FRWScopeLockWithPriority(Private::FRWLockWithPriority& InLockObject,
FRWScopeLockType InLockType, Private::ELockPriority InPriority = Private::PriorityHigh)
: Lock(InLockObject)
, Priority(InPriority)
, LockType(InLockType)
{
FScopedHighPriorityWaitTracker Tracker(&Lock.HighPriorityWaitersCount, Priority);
GuardWrapper.Emplace(Lock, LockType);
}
// NOTE: As the name suggests, this function should be used with caution.
// It releases the read lock _before_ acquiring a new write lock. This is not an atomic operation and the caller should
// not treat it as such.
// E.g. Pointers read from protected data structures prior to this call may be invalid after the function is called.
void ReleaseReadOnlyLockAndAcquireWriteLock_USE_WITH_CAUTION()
{
if (LockType == SLT_ReadOnly)
{
GuardWrapper.Reset();
{
FScopedHighPriorityWaitTracker Tracker(&Lock.HighPriorityWaitersCount, Priority);
GuardWrapper.Emplace(Lock, SLT_Write);
}
LockType = SLT_Write;
}
}
Private::FRWLockWithPriority& Lock;
TOptional<TRWScopeLock<Private::FRWLockWithPriority>> GuardWrapper;
Private::ELockPriority Priority;
FRWScopeLockType LockType;
};
namespace Impl
{
/** Container for global class inheritance data; the AssetRegistry may use a persistent buffer or a stack local buffer. */
struct FClassInheritanceBuffer
{
/** Map from Class->SuperClass for all classes including native classes and blueprint classes. Updated on demand. */
TMap<FTopLevelAssetPath, FTopLevelAssetPath> InheritanceMap;
/** Map from Class->(All subclasses) for all classes including native classes and blueprint classes. Updated on demand. */
TMap<FTopLevelAssetPath, TArray<FTopLevelAssetPath>> ReverseInheritanceMap;
/** Snapshot of GetCurrentAllClassesVersionNumber() at the time of the last update, to invalidate on changes to classes. */
uint64 SavedAllClassesVersionNumber = MAX_uint64;
/** Dirty flag to invalidate on other changes requiring a recompute. */
bool bDirty = true;
/** Report whether the dirty flag and other checks indicate this buffer does not need to be updated. */
bool IsUpToDate(uint64 CurrentAllClassesVersionNumber) const;
/** Delete data and free allocations. */
void Clear();
/** Report size of dynamic allocations. */
SIZE_T GetAllocatedSize() const;
};
/** Status of gathering, returned from the Tick function */
enum class EGatherStatus : uint8
{
TickActiveGatherActive,
TickActiveGatherIdle,
TickGameThreadActiveGatherIdle,
Complete,
UnableToProgress,
WaitingForEvents,
};
/** Affects how rules are applied to improve loading/runtime performance */
enum EPerformanceMode : uint8
{
// Handling slow async load
BulkLoading,
// Not changing, optimize for runtime queries
MostlyStatic,
};
/** Provides handling for time slicing during TickGatherer */
struct FInterruptionContext
{
public:
typedef TFunction<bool(void)> ShouldExitEarlyCallbackType;
FInterruptionContext() = default;
explicit FInterruptionContext(double InTickStartTime, double InMaxRunningTime)
: TickStartTime(InTickStartTime)
, MaxRunningTime(InMaxRunningTime)
{
}
FInterruptionContext(double InTickStartTime, double InMaxRunningTime, ShouldExitEarlyCallbackType& Callback)
: TickStartTime(InTickStartTime)
, MaxRunningTime(InMaxRunningTime)
, EarlyExitCallback(Callback) {}
void SetEarlyExitCallback(const ShouldExitEarlyCallbackType& InCallback)
{
EarlyExitCallback = InCallback;
}
void SetUnlimitedTickTime()
{
TickStartTime = -1.;
MaxRunningTime = -1.;
}
void SetLimitedTickTime(double InTickStartTime, double InMaxRunningTime)
{
TickStartTime = InTickStartTime;
MaxRunningTime = InMaxRunningTime;
}
double GetTickStartTime() const { return TickStartTime; }
bool IsTimeSlicingEnabled() const { return TickStartTime > 0; }
bool WasInterrupted() const { return OutInterrupted; }
bool ShouldExitEarly();
void RequestEarlyExit() { OutInterrupted = true; }
private:
// A negative value disables time slicing
double TickStartTime = -1.;
// The maximum time should allow before interruption. If TickStartTime is negative, this is ignored
double MaxRunningTime = -1.;
// If provided, this is always checked
ShouldExitEarlyCallbackType EarlyExitCallback;
// True if we ran out of time, EarlyExitCallback returned true, or RequestEarlyExit() was called
bool OutInterrupted = false;
};
} // namespace Impl within namespace UE::AssetRegistry
} // namespace UE::AssetRegistry
/** Returns true if ANY work remains to be done. This work might require the game thread. */
FORCEINLINE bool IsTickActive(UE::AssetRegistry::Impl::EGatherStatus Status)
{
return (Status == UE::AssetRegistry::Impl::EGatherStatus::TickGameThreadActiveGatherIdle)
|| (Status == UE::AssetRegistry::Impl::EGatherStatus::TickActiveGatherActive)
|| (Status == UE::AssetRegistry::Impl::EGatherStatus::TickActiveGatherIdle);
}
namespace UE::AssetRegistry
{
/**
* Threading helper class for UAssetRegistryImpl that holds all of the data.
* This class is not threadsafe; all const functions must be called from within a ReadLock and all non-const
* from within a WriteLock.
*/
class FAssetRegistryImpl
{
using FAssetDataMap = UE::AssetRegistry::Private::FAssetDataMap;
using FCachedAssetKey = UE::AssetRegistry::Private::FCachedAssetKey;
public:
/** Constructor initializes as little as possible; meaningful operations are first done during Initialize. */
FAssetRegistryImpl();
/** Construct the AssetRegistryImpl, including initial scans if applicable. */
void Initialize(Impl::FInitializeContext& Context);
/** Event for when duplicated assets are found and needs to be resolved */
IAssetRegistry::FAssetCollisionEvent& OnAssetCollision_Private();
// Helpers for functions of the same name from UAssetRegistryImpl
bool HasAssets(const FName PackagePath, const bool bRecursive) const;
FSoftObjectPath GetRedirectedObjectPath(const FSoftObjectPath& ObjectPath, UE::AssetRegistry::Impl::FEventContext* EventContext,
UE::AssetRegistry::Impl::FClassInheritanceContext* InheritanceContext, bool bNeedsScanning);
bool GetAncestorClassNames(Impl::FClassInheritanceContext& InheritanceContext, FTopLevelAssetPath ClassName,
TArray<FTopLevelAssetPath>& OutAncestorClassNames) const;
void CompileFilter(Impl::FClassInheritanceContext& InheritanceContext, const FARFilter& InFilter,
FARCompiledFilter& OutCompiledFilter) const;
void SetTemporaryCachingMode(bool bEnable);
void SetTemporaryCachingModeInvalidated();
bool AddPath(Impl::FEventContext& EventContext, FStringView PathToAdd);
void SearchAllAssets(Impl::FEventContext& EventContext, Impl::FClassInheritanceContext& InheritanceContext,
bool bSynchronousSearch);
bool GetVerseFilesByPath(FName PackagePath, TArray<FName>* OutFilePaths, bool bRecursive) const;
void ScanPathsSynchronous(Impl::FScanPathContext& Context);
void PrioritizeSearchPath(const FString& PathToPrioritize);
void ScanModifiedAssetFiles(Impl::FEventContext& EventContext, Impl::FClassInheritanceContext& InheritanceContext,
const TArray<FString>& InFilePaths, UE::AssetRegistry::EScanFlags ScanFlags);
void Serialize(FArchive& Ar, Impl::FEventContext& EventContext);
void AppendState(Impl::FEventContext& EventContext, const FAssetRegistryState& InState,
FAssetRegistryState::EInitializationMode Mode = FAssetRegistryState::EInitializationMode::Append, bool bEmitAssetEvents = false);
void GetAllocatedSize(bool bLogDetailed, SIZE_T& StateSize, SIZE_T& StaticSize, SIZE_T& SearchSize) const;
bool IsLoadingAssets() const;
bool IsGathering() const;
bool HasSerializedDiscoveryCache() const;
void SetManageReferences(FSetManageReferencesContext& Context);
bool SetPrimaryAssetIdForObjectPath(Impl::FEventContext& EventContext, const FSoftObjectPath& ObjectPath, FPrimaryAssetId PrimaryAssetId);
bool ResolveRedirect(const FString& InPackageName, FString& OutPackageName) const;
#if WITH_EDITOR
void OnDirectoryChanged(Impl::FEventContext& EventContext, Impl::FClassInheritanceContext& InheritanceContext,
TArray<FFileChangeData>& FileChangesProcessed);
void OnDirectoryRescanRequired(Impl::FEventContext& EventContext,
Impl::FClassInheritanceContext& InheritanceContext, FString& DirPath, int64 BeforeTimeStamp);
void AddLoadedAssetToProcess(const UObject& AssetLoaded);
#endif
void OnContentPathMounted(Impl::FEventContext& EventContext, Impl::FClassInheritanceContext& InheritanceContext,
const FString& InAssetPath, const FString& AssetPathWithTrailingSlash, const FString& FileSystemPath);
void OnContentPathDismounted(Impl::FEventContext& EventContext, const FString& InAssetPath,
const FString& AssetPathNoTrailingSlash, const FString& FileSystemPath);
// Other helper functions called by UAssetRegistryImpl
/**
* Update cached values about classes, and enable some global multithreaded access,
* when all startup plugins finish loading.
*/
void OnPluginLoadingComplete(bool bPhaseSuccessful);
/** Update cached values about native classes after they have possibly changed. */
void RefreshNativeClasses();
/** Enumerate assets in the State, filtering by filter and package not in PackagesToSkip */
void EnumerateDiskAssets(const FARCompiledFilter& InFilter, TSet<FName>& PackagesToSkip,
TFunctionRef<bool(const FAssetData&)> Callback, UE::AssetRegistry::EEnumerateAssetsFlags InEnumerateFlags) const;
/** Enumerate all assets in the State. PackageNamesToSkip are filtered. EEnumerateAssetsFlags provide additional filtering. */
void EnumerateAllDiskAssets(TSet<FName>& PackageNamesToSkip, TFunctionRef<bool(const FAssetData&)> Callback, UE::AssetRegistry::EEnumerateAssetsFlags InEnumerateFlags = UE::AssetRegistry::EEnumerateAssetsFlags::None) const;
/** Waits for the gatherer to be idle if it is operating synchronously. */
void WaitForGathererIdleIfSynchronous();
/** Waits for the gatherer to be idle. */
void WaitForGathererIdle(float TimeoutSeconds);
/** Consume any results from the gatherer and return its status */
Impl::EGatherStatus TickGatherer(Impl::FTickContext& TickContext);
/**
* At some times during editor startup, depending on config settings, we might want to block
* on the gather, and not tick the rest of the editor. When we want that to happen, we set
* this flag. This function can be read outside of the lock (we need to do that during Tick).
*/
bool IsGameThreadTakeOverGatherEachTick() const;
void SetGameThreadTakeOverGatherEachTick(bool bValue);
/** Send a log message with the search statistics.
* StartTime is used to report wall clock search time in the case of background scan
*/
void LogSearchDiagnostics(double StartTime);
/** Look for and load a single AssetData result from the gatherer. */
void TickGatherPackage(Impl::FEventContext& EventContext, const FString& PackageName, const FString& LocalPath);
void ClearGathererCache();
#if WITH_EDITOR
void AssetsSaved(UE::AssetRegistry::Impl::FEventContext& EventContext, TArray<FAssetData>&& Assets);
void GetProcessLoadedAssetsBatch(TArray<const UObject*>& OutLoadedAssets, uint32 BatchSize,
bool bUpdateDiskCacheAfterLoad);
void PushProcessLoadedAssetsBatch(Impl::FEventContext& EventContext,
TArrayView<FAssetData> LoadedAssetDatas, TArrayView<const UObject*> UnprocessedFromBatch);
/** Call LoadCalculatedDependencies on each Package updated after the last LoadCalculatedDependencies. */
void LoadCalculatedDependencies(TArray<FName>* AssetPackageNamessToCalculate, Impl::FClassInheritanceContext& InheritanceContext,
TSet<FName>* InPackagesNeedingDependencyCalculation, Impl::FInterruptionContext& InOutInterruptionContext);
/** For each package in BackgroundPackages, if there is a registered gatherer for its asset class, this function will
* move the package to GameThreadPackages otherwise it will be removed from the set. Any entries in GameThreadPackages that
* do not have a corresponding registered gatherer will likewise be removed. The end condition for this function is that
* BackgroundPackages is empty and GameThreadPackages contains only those packages which require LoadCalculatedDependencies to
* be called on them
*/
void PruneAndCoalescePackagesRequiringDependencyCalculation(TSet<FName>& BackgroundPackages, TSet<FName>& GameThreadPackages,
Impl::FInterruptionContext& InOutInterruptionContext);
/** Look for a CalculatedDependencies function registered for the asset(s) in the given package
* and call that function to add calculated dependencies. Calculated dependencies are added only after
* all normal dependencies gathered from the AssetRegistry data stored in the package have been loaded.
*/
void LoadCalculatedDependencies(FName PackageName, Impl::FClassInheritanceContext& InheritanceContext,
bool& bOutHadActivity);
/** Add a watch on a directory that to modify data for a package whenever packages in the directory are modified. */
void AddDirectoryReferencer(FName PackageName, const FString& DirectoryLocalPathOrLongPackageName);
/** Set the directories watched by PackageName. Removes previous list, call with empty array to clear. */
void SetDirectoriesWatchedByPackage(FName PackageName, TConstArrayView<FString> Directories);
/** Called when new gatherer is registered. Requires subsequent call to RebuildAssetDependencyGathererMapIfNeeded */
void OnAssetDependencyGathererRegistered() { bRegisteredDependencyGathererClassesDirty = true; }
void ApplyObjectRedirectsToGatheredAssetDatas();
#endif
/** Adds an asset to the empty package list which contains packages that have no assets left in them */
void AddEmptyPackage(FName PackageName);
/** Removes an asset from the empty package list because it is no longer empty */
bool RemoveEmptyPackage(FName PackageName);
/** Adds a path to the cached paths tree. Returns true if the path was added and did not already exist */
bool AddAssetPath(Impl::FEventContext& EventContext, FName PathToAdd);
/** Removes a path to the cached paths tree. Returns true if successful. */
bool RemoveAssetPath(Impl::FEventContext& EventContext, FName PathToRemove, bool bEvenIfAssetsStillExist = false);
/** Removes the asset data associated with this package from the look-up maps */
void RemovePackageData(Impl::FEventContext& EventContext, const FName PackageName);
/** Adds the Verse file to the look up maps */
void AddVerseFile(Impl::FEventContext& EventContext, FName VerseFilePathToAdd);
/** Removes the Verse file from the look-up maps */
void RemoveVerseFile(Impl::FEventContext& EventContext, FName VerseFilePathToRemove);
/** Returns the names of all subclasses of the class whose name is ClassName */
void GetSubClasses(Impl::FClassInheritanceContext& InheritanceContext, const TArray<FTopLevelAssetPath>& InClassNames,
const TSet<FTopLevelAssetPath>& ExcludedClassNames, TSet<FTopLevelAssetPath>& SubClassNames) const;
bool IsInitialSearchCompleted() const { return bInitialSearchCompleted.load(std::memory_order_relaxed); }
bool IsTempCachingEnabled() const { return bIsTempCachingEnabled; }
bool IsTempCachingAlwaysEnabled() const { return bIsTempCachingAlwaysEnabled; }
bool IsInitialSearchStarted() const { return bInitialSearchStarted; }
bool IsAdditionalMountSearchInProgress() const { return bAdditionalMountSearchInProgress.load(std::memory_order_relaxed); }
bool IsSearchAllAssets() const { return bSearchAllAssets; }
Impl::FClassInheritanceBuffer& GetTempCachedInheritanceBuffer() { return TempCachedInheritanceBuffer; }
uint64 GetSavedGeneratorClassesVersionNumber() const { return SavedGeneratorClassesVersionNumber; }
uint64 GetSavedAllClassesVersionNumber() const { return SavedAllClassesVersionNumber; }
static uint64 GetCurrentGeneratorClassesVersionNumber();
static uint64 GetCurrentAllClassesVersionNumber();
/** Get a copy of the cached serialization options that were parsed from ini */
void CopySerializationOptions(FAssetRegistrySerializationOptions& OutOptions, ESerializationTarget Target) const;
/** Query the performance mode, which modifies how data structures are loaded */
Impl::EPerformanceMode GetPerformanceMode() const { return PerformanceMode; }
void SetPerformanceMode(Impl::EPerformanceMode NewMode);
bool ShouldSortDependencies() const;
bool ShouldSortReferencers() const;
const FAssetRegistryState& GetState() const;
const FPathTree& GetCachedPathTree() const;
const TSet<FName>& GetCachedEmptyPackages() const;
/** Same as UE::AssetRegistry::Filtering::ShouldSkipAsset, but can be read from any thread under readlock */
bool ShouldSkipAsset(FTopLevelAssetPath AssetClass, uint32 PackageFlags) const;
bool ShouldSkipAsset(const UObject* InAsset) const;
/** Finds all class names of classes capable of generating new UClasses */
void CollectCodeGeneratorClasses();
/**
* Block until the Premade AssetRegistry finishes loading, if there is one still loading.
* For performance reasons, this must be called within the WriteScopeLock, and the caller
* must handle the possibility that it leaves and reenters the lock.
*/
void ConditionalLoadPremadeAssetRegistry(UAssetRegistryImpl& UARI,
UE::AssetRegistry::Impl::FEventContext& EventContext, FInterfaceWriteScopeLock& ScopeLock);
#if WITH_EDITOR
/** Request to pause or resume background processing of scan results.
* This can be used to allow a priority thread to perform along sequence of operations
* without having to contend with the background thread for data access
*/
void RequestPauseBackgroundProcessing();
void RequestResumeBackgroundProcessing();
bool IsBackgroundProcessingPaused() const;
uint32& GetBackgroundTickInterruptionsCount();
#endif
private:
/** The delegate to execute when an asset collision is detected */
IAssetRegistry::FAssetCollisionEvent AssetCollisionEvent;
/**
* Enumerate all AssetData in memory and in the State that match the given path.
* The returned AssetData for objects present in memory have no AssetRegistryTags calculated
*/
void EnumerateAssetsByPathNoTags(FName PackagePath,
TFunctionRef<bool(const FAssetData&)> Callback, bool bRecursive, bool bIncludeOnlyOnDiskAssets) const;
/** Create redirectors read by this->GetRedirectedObjectPath, based on plugin settings */
void InitRedirectors(Impl::FEventContext& EventContext, Impl::FClassInheritanceContext& InheritanceContext,
bool& bOutRedirectorsNeedSubscribe);
/** If collecting dependencies, create an FAssetPackageData for every script package, to make sure dependencies can find a data for them. */
void ReadScriptPackages();
/* Try to construct the gatherer if it does not already exist. If that is not possible (e.g., because we are shutting down)
* return false. If it was successfully created or already existed, return true.
*/
bool TryConstructGathererIfNeeded();
/** Called to set up timing variables and launch the on-constructor SearchAllAssets async call */
void SearchAllAssetsInitialAsync(Impl::FEventContext& EventContext, Impl::FClassInheritanceContext& InheritanceContext);
/**
* Called every tick to when data is retrieved by the background asset search.
* If TickStartTime is < 0, the entire list of gathered assets will be cached. Also used in sychronous searches
* The DeferredResults array contains assets that were not ready for processing due to required UClass's not being loaded
*/
void AssetSearchDataGathered(Impl::FEventContext& EventContext,
TMultiMap<FName, TUniquePtr<FAssetData>>& AssetResults,
TMultiMap<FName, TUniquePtr<FAssetData>>& OutDeferredResults,\
Impl::FInterruptionContext& InOutInterruptionContext,
TOptional<TSet<FString>>& MountPointsForVerifyAfterGather);
/** Validate assets gathered from disk before adding them to the AssetRegistry. */
bool ShouldSkipGatheredAsset(FAssetData& AssetData);
/**
* Called every tick when data is retrieved by the background path search.
* If TickStartTime is < 0, the entire list of gathered assets will be cached. Also used in sychronous searches
*/
void PathDataGathered(Impl::FEventContext& EventContext, TRingBuffer<FString>& PathResults,
Impl::FInterruptionContext& InOutInterruptionContext,
TOptional<TSet<FString>>& MountPointsForVerifyAfterGather);
/** Called every tick when data is retrieved by the background dependency search */
void DependencyDataGathered(TMultiMap<FName, FPackageDependencyData>& DependsResults,
TMultiMap<FName, FPackageDependencyData>& OutDeferredDependencyResults,
TSet<FName>* OutPackagesNeedingDependencyCalculation, Impl::FInterruptionContext& InOutInterruptionContext,
TOptional<TSet<FString>>& MountPointsForVerifyAfterGather);
/** Called every tick when data is retrieved by the background search for cooked packages that do not contain asset data */
void CookedPackageNamesWithoutAssetDataGathered(Impl::FEventContext& EventContext,
TRingBuffer<FString>& CookedPackageNamesWithoutAssetDataResults, Impl::FInterruptionContext& InOutInterruptionContext);
/** Called every tick when data is retrieved by the background dependency search */
void VerseFilesGathered(Impl::FEventContext& EventContext, TRingBuffer<FName>& VerseResults, Impl::FInterruptionContext& InOutInterruptionContext);
/** Adds the asset data to the lookup maps */
void AddAssetData(Impl::FEventContext& EventContext, FAssetData* AssetData);
/** Updates an existing asset data with the new value and updates lookup maps */
void UpdateAssetData(Impl::FEventContext& EventContext, FAssetData* AssetData, FAssetData&& NewAssetData, bool bKeepDeletedTags);
/** Updates the tags on an existing asset data by adding the tags from NewAssetData that do not already exist. */
void AddNonOverlappingTags(Impl::FEventContext& EventContext, FAssetData& ExistingAssetData,
const FAssetData& NewAssetData);
/** Removes the asset data from the lookup maps */
bool RemoveAssetData(Impl::FEventContext& EventContext, FAssetData* AssetData);
#if WITH_EDITOR
/** Called when two assets have the same ID but different packages. Logs it and returns which one to keep. */
FAssetData* ResolveAssetIdCollision(FAssetData& A, FAssetData& B);
/**
* Calls PostLoadAssetRegistryTags on the CDO of the asset class this data represents.
* @param AssetData Existing asset data
* @return Returns false if the required parent UClass is not yet available
*/
bool TryPostLoadAssetRegistryTags(FAssetData* AssetData);
/** If new gatherers were registered, this call will refresh the mapping */
void RebuildAssetDependencyGathererMapIfNeeded();
/** When the scan is completed, resolve the redirections in CachedBPInheritanceMap. */
void ResolveRedirectionsInCachedBPInheritanceMap();
#endif // WITH_EDITOR
void GetSubClasses_Recursive(Impl::FClassInheritanceContext& InheritanceContext, FTopLevelAssetPath InClassName,
TSet<FTopLevelAssetPath>& SubClassNames, TSet<FTopLevelAssetPath>& ProcessedClassNames, const TSet<FTopLevelAssetPath>& ExcludedClassNames) const;
/** Internal helper which processes a given state and adds its contents to the current registry */
void CachePathsFromState(Impl::FEventContext& EventContext, const FAssetRegistryState& InState);
/** Adds ClassPath-> ParentPath to CachedBPInheritanceMap. Applies CoreRedirects to NotYetRedirectedParentPath. */
void AddCachedBPClassParent(const FTopLevelAssetPath& ClassPath, const FTopLevelAssetPath& NotYetRedirectedParentPath);
/** Updates OutBuffer from loaded classes and registered-in-CachedBPInheritanceMap blueprint classes */
void UpdateInheritanceBuffer(Impl::FClassInheritanceBuffer& OutBuffer) const;
/** Conditionally loads the Premade AssetRegistry from disk, or queues it for asynchronous loading if not yet loaded. */
void ConsumeOrDeferPreloadedPremade(UAssetRegistryImpl& UARI, Impl::FEventContext& EventContext);
/** Moves a premade asset registry state into this AR */
void LoadPremadeAssetRegistry(Impl::FEventContext& Context,
Premade::ELoadResult LoadResult, FAssetRegistryState&& ARState);
/** Add MountPoints of all AssetDatas currently registered in this->State to the list of PersistentMountPoints. */
void UpdatePersistentMountPoints();
void OnInitialSearchCompleted(Impl::FEventContext& EventContext);
void OnAdditionalMountSearchCompleted(Impl::FEventContext& EventContext);
/** This function is not called and, currently, will always return 'true'. See the commented out call in
* FAssetDataGatherer::TickInternal for further details on how it could be used.
*/
friend FAssetDataGatherer;
bool ClassRequiresGameThreadProcessing(const UClass* Class) const;
friend UAssetRegistryImpl;
/** This exists purely for use during shutdown to enable the UAssetRegistryImpl to
* avoid waiting for the gatherer to terminate while holding the interface lock.
* See UAssetRegistryImpl::OnEnginePreExit
*/
TUniquePtr<FAssetDataGatherer>& AccessGlobalGatherer() { return GlobalGatherer; }
void UpdateMaxSecondsPerFrame();
/** Free slack memory accumulated by the asset registry. */
void Shrink();
private:
/** Internal state of the cached asset registry */
FAssetRegistryState State;
/** Database of known Verse files */
TSet<FName> CachedVerseFiles;
TMap<FName, TArray<FName>> CachedVerseFilesByPath;
/** Default options used for serialization */
FAssetRegistrySerializationOptions SerializationOptions;
FAssetRegistrySerializationOptions DevelopmentSerializationOptions;
/** The set of empty package names (packages which contain no assets but have not yet been saved) */
TSet<FName> CachedEmptyPackages;
/** The map of classes to their parents, only full for offline blueprints */
TMap<FTopLevelAssetPath, FTopLevelAssetPath> CachedBPInheritanceMap;
/** If true, search caching is enabled */
bool bIsTempCachingEnabled;
/** If true, search caching is enabled permanently */
bool bIsTempCachingAlwaysEnabled;
/** Persistent InheritanceBuffer used when SetTemporaryCachingMode is on */
Impl::FClassInheritanceBuffer TempCachedInheritanceBuffer;
uint64 SavedGeneratorClassesVersionNumber;
uint64 SavedAllClassesVersionNumber;
/** The tree of known cached paths that assets may reside within */
FPathTree CachedPathTree;
/** Async task that gathers asset information from disk */
TUniquePtr<FAssetDataGatherer> GlobalGatherer;
/** Lists of results from the gatherer thread that are waiting to get processed */
UE::AssetDataGather::FResults BackgroundResults;
/** Assets and dependencies that are not ready for processing because they can't yet run PostLoadAssetRegistrytags */
TMultiMap<FName, TUniquePtr<FAssetData>> DeferredAssets;
TMultiMap<FName, TUniquePtr<FAssetData>> DeferredAssetsForGameThread;
TMultiMap<FName, FPackageDependencyData> DeferredDependencies;
TMultiMap<FName, FPackageDependencyData> DeferredDependenciesForGameThread;
#if !NO_LOGGING
/** Memory profiling information: How much memory is being used by the tags for each class. */
TMap<FTopLevelAssetPath, int64> TagSizeByClass;
#endif
/**
* MountPoints, in the format of LongPackageName with no trailing slash, that should not have their
* AssetDatas removed even if the MountPoint is dismounted.
*/
TSet<FName> PersistentMountPoints;
/** Time spent processing Gather results */
float StoreGatherResultsTimeSeconds;
/** The highest number of pending results observed during initial gathering */
int32 HighestPending = 0;
/** Time the initial async search was started */
double InitialSearchStartTime = 0.0f;
/** Time the additional mount async search was started */
double AdditionalMountSearchStartTime = 0.0f;
/** Flag to indicate if we used an initial async search */
bool bInitialSearchStarted;
/** Flag to indicate if the initial background search has completed. All access are relaxed because
* the actual search data can only be accessed under a proper lock.
*/
std::atomic<bool> bInitialSearchCompleted;
/** Flag to indicate if an additional mount background search has started after the initial search.
* All access are relaxed because the actual search data can only be accessed under a proper lock.
*/
std::atomic<bool> bAdditionalMountSearchInProgress;
/**
* Flag to indicate PreloadingComplete; finishing the background search is blocked until preloading complete
* because preloading can add assets.
*/
bool bPreloadingComplete = false;
/** Status of the background search, so we can take actions when it changes to or from idle */
Impl::EGatherStatus GatherStatus;
/** What kind of performance mode this is in, used to optimize for initial loading vs runtime */
Impl::EPerformanceMode PerformanceMode;
/**
* Enables extra check to make sure path still mounted before adding.
* Removing mount point can happen between scan (background thread + multiple ticks and the add).
*/
bool bVerifyMountPointAfterGather;
/** Record whether SearchAllAssets has been called; if so we will also search new mountpoints when added */
bool bSearchAllAssets;
bool bVerboseLogging;
bool bForceCompletionEvenIfPostLoadsFail = false;
bool bProcessedAnyAssetsAfterRetryDeferred = true;
float MaxSecondsPerFrame = 0.04f;
/** List of all class names derived from Blueprint (including Blueprint itself) */
TSet<FTopLevelAssetPath> ClassGeneratorNames;
struct FAssetRegistryPackageRedirect
{
public:
FAssetRegistryPackageRedirect(const FString& InSourcePackageName, const FString& InDestPackageName)
: SourcePackageName(InSourcePackageName), DestPackageName(InDestPackageName)
{
}
FString SourcePackageName;
FString DestPackageName;
};
TArray<FAssetRegistryPackageRedirect> PackageRedirects;
#if WITH_EDITOR
/**
* The set of object paths that have had their dependencies gathered since the last idle,
* and that need to check for calculated dependencies at the next idle.
*/
TSet<FName> PackagesNeedingDependencyCalculation;
TSet<FName> PackagesNeedingDependencyCalculationOnGameThread;
/** List of objects that need to be processed because they were loaded or saved */
TRingBuffer<TWeakObjectPtr<const UObject>> LoadedAssetsToProcess;
/** The set of object paths that have had their disk cache updated from the in memory version */
TSet<FSoftObjectPath> AssetDataObjectPathsUpdatedOnLoad;
/**
* A bidirectional map of which directories are watched by which packages for CalculatedDependencies. When a
* directory updates we need to rerun CalculatedDependencies for every package watching the directory. When a
* package runs CalculatedDependencies, we need to clear all of its watched files.
*/
TMap<FString, TSet<FName>> PackagesWatchingDirectory;
TMap<FName, TArray<FString>> DirectoriesWatchedByPackage;
/**
* Number of times during the startup scan that TickOnBackgroundThread was interrupted by a request for the
* AssetRegistry's lock from another thread.
*/
uint32 BackgroundTickInterruptionsCount = 0;
std::atomic<bool> bGameThreadTakeOverGatherEachTick;
/** A map of per asset class dependency gatherer called in LoadCalculatedDependencies */
TMultiMap<FTopLevelAssetPath, UE::AssetDependencyGatherer::Private::FRegisteredAssetDependencyGatherer*> RegisteredDependencyGathererClasses;
mutable FRWLock RegisteredDependencyGathererClassesLock;
bool bRegisteredDependencyGathererClassesDirty;
bool bGatherNeedsApplyObjectRedirects = false;
#endif
#if WITH_ENGINE && WITH_EDITOR
/** Class names that return true for IsAsset but which should not be treated as assets in uncooked packages */
TSet<FTopLevelAssetPath> SkipUncookedClasses;
/** Class names that return true for IsAsset but which should not be treated as assets in cooked packages */
TSet<FTopLevelAssetPath> SkipCookedClasses;
#endif
UE::AssetRegistry::Premade::FAsyncConsumer AsyncConsumer;
friend UE::AssetRegistry::Premade::FAsyncConsumer;
friend UE::AssetRegistry::Impl::FClassInheritanceContext;
friend UE::AssetRegistry::Impl::FTickContext;
};
namespace Impl
{
/**
* Stores events that need to be broadcasted from the AssetRegistry; these events are added to
* by functions within a lock, and are broadcast after the lock is dropped. The broadcast is also deferred
* until on the GameThread.
*/
struct FEventContext
{
enum class EEvent : uint32
{
Added,
Removed,
Updated,
UpdatedOnDisk,
MAX
};
TOptional<IAssetRegistry::FFileLoadProgressUpdateData> ProgressUpdateData;
TArray<TPair<FString, EEvent>> PathEvents;
TArray<TPair<FAssetData, EEvent>> AssetEvents;
TArray<TPair<FName, EEvent>> VerseEvents;
TArray<FString> RequiredLoads;
TArray<FString> BlockedFiles;
bool bFileLoadedEventBroadcast = false;
bool bScanStartedEventBroadcast = false;
bool bHasSentFileLoadedEventBroadcast = false;
bool bKnownGathersCompleteEventBroadcast = false;
/** Remove all stored events */
void Clear();
/** Report whether this context has any stored events */
bool IsEmpty() const;
/** Add all events from other onto this context's collection of events */
void Append(FEventContext&& Other);
};
/*
* An accessor for the inheritance map and related data for functions that need them; the source
* of this data is an internal detail unneeded by the functions reading it. Readers of the data call
* ConditionalUpdate to ensure it is up to date before reading it.
*/
struct FClassInheritanceContext
{
/** The Buffer providing the data for this context. */
FClassInheritanceBuffer* Buffer = nullptr;
/** Back pointer to the AssetRegistryImpl to update the Buffer if necessary */
FAssetRegistryImpl* AssetRegistryImpl = nullptr;
/** Whether ConditionalUpdate needs to update inheritance data */
bool bInheritanceMapUpToDate = false;
/** Whether ConditionalUpdate needs to update code generate classes before it can update inheritance data */
bool bCodeGeneratorClassesUpToDate = false;
/** Set the context to use the data from the given buffer */
void BindToBuffer(FClassInheritanceBuffer& InBuffer, FAssetRegistryImpl& InAssetRegistryImpl,
bool bInInheritanceMapUpToDate, bool bInCodeGeneratorClassesUpToDate);
/** Update the backing buffer if it is out of date */
void ConditionalUpdate();
};
/** Context to transform, collect, and pass along ScanPathsSynchronous arguments and results */
struct FScanPathContext
{
FScanPathContext(FEventContext& InEventContext, FClassInheritanceContext& InInheritanceContext,
const TArray<FString>& InDirs, const TArray<FString>& InFiles,
UE::AssetRegistry::EScanFlags InScanFlags = UE::AssetRegistry::EScanFlags::None,
TArray<FSoftObjectPath>* FoundAssets = nullptr);
TArray<FString> PackageDirs;
TArray<FString> LocalDirs;
TArray<FString> PackageFiles;
TArray<FString> LocalFiles;
TArray<FString> LocalPaths;
FEventContext& EventContext;
FClassInheritanceContext& InheritanceContext;
TArray<FSoftObjectPath>* OutFoundAssets = nullptr;
int32 NumFoundAssets = 0;
bool bForceRescan = false;
bool bIgnoreDenyListScanFilters = false;
bool bIgnoreInvalidPathWarning = false;
EGatherStatus Status = EGatherStatus::TickActiveGatherActive;
};
/** Callback types for FTickContext */
typedef TFunctionRef<void(const TMultiMap<FName, FAssetData*>&)> FAssetsFoundCallback;
typedef TFunctionRef<void(const TRingBuffer<FName>&)> FVerseFilesFoundCallback;
/** Input and output variables for the TickGatherer function. */
struct FTickContext
{
public:
FTickContext(FAssetRegistryImpl& InGuardedData, Impl::FEventContext& InEventContext,
Impl::FClassInheritanceContext& InInheritanceContext);
/** Start the timer to measure time spent in the current call to TickGatherer, if not already started. */
void LazyStartTimer();
/**
* Record the time that was spent in the current call to TickGatherer onto the metric for it on GuardedData, if
* LazyStartTimer has been called and RecordTimer has not already been called.
*/
void RecordTimer();
/**
* Return progress-bar metric for how many elements remain in the various lists of work to do. A portion of this
* calculation is cached (the GetNumGatherFromDiskPending) and it will not get the most up to date data until
* SetNumGatherFromDiskPendingDirty is called.
*/
int32 GetNumPending();
/**
* Return the portion of GetNumPending that is handled during gathering from disk; does not include the
* calculations that we do once the gather from disk is done. This function is cached, it does not redo the
* calculation until SetNumPendingDirty is called.
*/
int32 GetNumGatherFromDiskPending();
/** Invalidate the cache in GetNumGatherFromDiskPending */
void SetNumGatherFromDiskPendingDirty();
/** Run the expression to calculate the value used in GetNumGatherFromDiskPending. */
int32 CalculateNumGatherFromDiskPending();
/**
* Store the result of TickGatherer in the AssetRegistry's status variable and send the progress event.
* Returns the input Status so it can be used in a return line.
*/
EGatherStatus SetIntermediateStatus(EGatherStatus Status);
/**
* Calculate the result of TickGatherer from the current gather data on the TickContext and call
* SetIntermediateStatus. Returns the input Status so it can be used in a return line.
*/
EGatherStatus UpdateIntermediateStatus();
void RunAssetSearchDataGathered(TMultiMap<FName, TUniquePtr<FAssetData>>& InAssetResults,
TMultiMap<FName, TUniquePtr<FAssetData>>& OutDeferredAssetResults);
void RunDependencyDataGathered(TMultiMap<FName, FPackageDependencyData>& DependenciesToProcess,
TMultiMap<FName, FPackageDependencyData>& OutDeferredDependencies,
TSet<FName>* OutPackagesNeedingDependencyCalculation);
public:
UE::AssetDataGather::FResultContext ResultContext;
Impl::FInterruptionContext InterruptionContext;
FAssetRegistryImpl& GuardedData;
Impl::FEventContext& EventContext;
Impl::FClassInheritanceContext& InheritanceContext;
TOptional<FAssetsFoundCallback> AssetsFoundCallback;
TOptional<FVerseFilesFoundCallback> VerseFilesFoundCallback;
TOptional<TSet<FString>> MountPointsForVerifyAfterGather;
double TimingStartTime = -1.;
int32 NumGatherFromDiskPending = 0;
bool bHandleCompletion = false;
bool bHandleDeferred = false;
bool bNumGatherFromDiskPendingDirty = true;
bool bHadAssetsToProcess = false;
bool bIsInGameThread = false;
};
} // namespace Impl within namespace UE::AssetRegistry
namespace Utils
{
/** Different modes for RunAssetThroughFilter and related filter functions */
enum class EFilterMode : uint8
{
/** Include things that pass the filter; include everything if the filter is empty */
Inclusive,
/** Exclude things that pass the filter; exclude nothing if the filter is empty */
Exclusive,
};
/** Report whether the external-code-created filter is in a state that is safe to use in filtering */
bool IsFilterValid(const FARCompiledFilter& Filter);
/** Report whether the given AssetData passes/fails the given filter */
bool RunAssetThroughFilter(const FAssetData& AssetData, const FARCompiledFilter& Filter, const EFilterMode FilterMode);
/** Helper for RunAssetThroughFilter and RunAssetsThroughFilter that skips validity check */
bool RunAssetThroughFilter_Unchecked(const FAssetData& AssetData, const FARCompiledFilter& Filter, const bool bPassFilterValue);
/**
* Given an array of asset data, trim the items that fail the filter based on the inclusion/exclusion mode used.
* - In inclusive mode it will remove all assets that fail the filter, and in exclusive mode it will remove all assets that pass the filter.
* - If the filter is empty, then the array will be untouched.
*/
void RunAssetsThroughFilter(TArray<FAssetData>& AssetDataList, const FARCompiledFilter& Filter, const EFilterMode FilterMode);
/** This will always read the ini, public version may return cache */
void InitializeSerializationOptionsFromIni(
FAssetRegistrySerializationOptions& Options,
const ITargetPlatform* TargetPlatform = nullptr,
ESerializationTarget Target = ESerializationTarget::ForGame);
/** Gets the current availability of an asset, primarily for streaming install purposes. */
EAssetAvailability::Type GetAssetAvailability(const FAssetData& AssetData);
/** Gets an ETA or percentage complete for an asset that is still in the process of being installed. */
float GetAssetAvailabilityProgress(const FAssetData& AssetData, EAssetAvailabilityProgressReportingType::Type ReportType);
/** Returns if a given report type is supported on the current platform */
bool GetAssetAvailabilityProgressTypeSupported(EAssetAvailabilityProgressReportingType::Type ReportType);
/** Hint the streaming installers to prioritize a specific asset for install. */
void PrioritizeAssetInstall(const FAssetData& AssetData);
/**
* Reads Asset file from a previously initialized package reader
*
* @param PackageReader Previously initialized package reader that maps to the data to be read
* @param AssetDataList List of the read asset data
*/
bool ReadAssetFile(FPackageReader& PackageReader, IAssetRegistry::FLoadPackageRegistryData& InOutData);
/** If InOutMountPoints is not already set, sets it and reads mount points into it from FPackageName. */
void InitializeMountPoints(TOptional<TSet<FString>>& InOutMountPoints);
/**
* Returns true if path belongs to one of the mount points provided
*
* @param Path Path to check if mounted, example "/MyPlugin/SomeAsset"
* @param MountPointsNoTrailingSlashes Mount points without the trailing slash. Example: "/MyPlugin"
* @param StringBuffer String buffer to avoid re-allocation performance hit when searching TSet
*/
bool IsPathMounted(const FString& Path, const TSet<FString>& MountPointsNoTrailingSlashes, FString& StringBuffer);
/**
* Iterate all UObjects where this->IsAsset and !ShouldSkipAsset, create an FAssetData for them and call the callback.
* Stores packagenames of all assets found in OutPackageNamesWithAssets for later filtering of on-disk assets.
* Stops iteration and sets bOutStopIteration=true iff the callback returns false.
*/
void EnumerateAllMemoryAssets(TSet<FName>& OutPackageNamesWithAssets, bool& bOutStopIteration, TFunctionRef<bool(FAssetData&&)> Callback);
/**
* Helper for EnumerateMemoryAssets* functions. Calls the given callback on all memory assets that pass the portion of the
* given filter that does not require tags, and passes in an FAssetData constructed from the UObject but missing tags.
* Fills in OutPackageNamesWithAssets with names of all packages tested.
*/
void EnumerateMemoryAssetsHelper(const FARCompiledFilter& InFilter, TSet<FName>& OutPackageNamesWithAssets,
bool& bOutStopIteration, TFunctionRef<bool(const UObject* Object, FAssetData&& PartialAssetData)> Callback,
bool bSkipARFilteredAssets);
/**
* Call the given callback on all UObjects in memory that pass the given filter.
* Fills in OutPackageNamesWithAssets with names of all packages tested.
*/
void EnumerateMemoryAssets(const FARCompiledFilter& InFilter, TSet<FName>& OutPackageNamesWithAssets,
bool& bOutStopIteration, UE::AssetRegistry::Private::FInterfaceRWLock& InterfaceLock, const FAssetRegistryState& GuardedDataState,
TFunctionRef<bool(FAssetData&&)> Callback, bool bSkipARFilteredAssets);
} // namespace Utils within namespace UE::AssetRegistry
// Normalization functions for paths. We require that normalized paths are case-insensitively string-identical for the same input
// path specification. e.g. all slashes must be replaced with forward-slashes. Special case: a leading \\ is left unreplaced,
// so //share will be seen as different than \\share.
FString CreateStandardFilename(const FString& InFilename);
} // namespace UE::AssetRegistry
///////////////////////////////////////////////////////
// Inline implementations
///////////////////////////////////////////////////////
namespace UE::AssetRegistry
{
#if WITH_EDITOR
inline uint32& FAssetRegistryImpl::GetBackgroundTickInterruptionsCount()
{
return BackgroundTickInterruptionsCount;
}
inline bool FAssetRegistryImpl::IsGameThreadTakeOverGatherEachTick() const
{
return bGameThreadTakeOverGatherEachTick.load(std::memory_order_relaxed);
}
inline void FAssetRegistryImpl::SetGameThreadTakeOverGatherEachTick(bool bValue)
{
bGameThreadTakeOverGatherEachTick.store(bValue, std::memory_order_relaxed);
}
#endif
} // namespace UE::AssetRegistry