2020-10-21 17:56:05 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2021-10-25 20:05:28 -04:00
# include "VirtualizationManager.h"
2020-10-21 17:56:05 -04:00
2021-06-03 04:35:17 -04:00
# include "HAL/PlatformTime.h"
2021-10-25 20:05:28 -04:00
# include "IVirtualizationBackend.h"
2022-04-22 08:55:44 -04:00
# include "Logging/MessageLog.h"
2021-04-30 08:14:54 -04:00
# include "Misc/CommandLine.h"
2020-10-21 17:56:05 -04:00
# include "Misc/ConfigCacheIni.h"
2021-10-25 20:05:28 -04:00
# include "Misc/CoreDelegates.h"
2021-11-07 23:43:01 -05:00
# include "Misc/PackageName.h"
# include "Misc/PackagePath.h"
2021-04-30 08:14:54 -04:00
# include "Misc/Parse.h"
2020-10-21 17:56:05 -04:00
# include "Misc/Paths.h"
2021-06-03 04:35:17 -04:00
# include "Misc/ScopeLock.h"
2022-03-08 11:22:45 -05:00
# include "PackageSubmissionChecks.h"
2021-06-03 04:35:17 -04:00
# include "ProfilingDebugging/CookStats.h"
2021-11-07 23:43:01 -05:00
# include "VirtualizationFilterSettings.h"
2022-04-22 08:55:44 -04:00
# define LOCTEXT_NAMESPACE "Virtualization"
2022-03-08 11:22:45 -05:00
2021-04-30 08:14:54 -04:00
namespace UE : : Virtualization
{
2021-10-25 20:05:28 -04:00
UE_REGISTER_VIRTUALIZATION_SYSTEM ( UE : : Virtualization : : FVirtualizationManager , Default ) ;
2021-04-30 08:14:54 -04:00
/** Utility struct, similar to FScopeLock but allows the lock to be enabled/disabled more easily */
struct FConditionalScopeLock
{
UE_NONCOPYABLE ( FConditionalScopeLock ) ;
FConditionalScopeLock ( FCriticalSection * InSyncObject , bool bShouldLock )
{
checkf ( InSyncObject ! = nullptr , TEXT ( " InSyncObject must point to a valid FCriticalSection " ) ) ;
if ( bShouldLock )
{
SyncObject = InSyncObject ;
SyncObject - > Lock ( ) ;
}
else
{
SyncObject = nullptr ;
}
}
/** Destructor that performs a release on the synchronization object. */
~ FConditionalScopeLock ( )
{
if ( SyncObject ! = nullptr )
{
SyncObject - > Unlock ( ) ;
}
}
private :
FCriticalSection * SyncObject ;
} ;
2022-02-16 01:27:09 -05:00
bool LexTryParseString ( EPackageFilterMode & OutValue , FStringView Buffer )
{
if ( Buffer = = TEXT ( " OptOut " ) )
{
OutValue = EPackageFilterMode : : OptOut ;
return true ;
}
else if ( Buffer = = TEXT ( " OptIn " ) )
{
OutValue = EPackageFilterMode : : OptIn ;
return true ;
}
return false ;
}
2021-04-30 08:14:54 -04:00
/* Utility function for building up a lookup table of all available IBackendFactory interfaces*/
2021-07-22 09:55:49 -04:00
FVirtualizationManager : : FRegistedFactories FindBackendFactories ( )
2021-04-30 08:14:54 -04:00
{
2021-07-22 09:55:49 -04:00
FVirtualizationManager : : FRegistedFactories BackendFactories ;
2021-04-30 08:14:54 -04:00
TArray < IVirtualizationBackendFactory * > FactoriesArray = IModularFeatures : : Get ( ) . GetModularFeatureImplementations < IVirtualizationBackendFactory > ( FName ( " VirtualizationBackendFactory " ) ) ;
for ( IVirtualizationBackendFactory * FactoryInterface : FactoriesArray )
{
checkf ( FactoryInterface ! = nullptr , TEXT ( " A nullptr was added to the modular features for 'VirtualizationBackendFactory' " ) ) ;
const FName FactoryName = FactoryInterface - > GetName ( ) ;
if ( ! BackendFactories . Contains ( FactoryName ) )
{
BackendFactories . Add ( FactoryName , FactoryInterface ) ;
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Duplicate IBackendFactory found! Name '%s' " ) , * FactoryName . ToString ( ) ) ;
}
}
return BackendFactories ;
}
/* Utility function for finding entries in a given string*/
TArray < FString > ParseEntries ( const FString & Data )
{
TArray < FString > Entries ;
const TCHAR * DataPtr = * Data ;
const TCHAR * EntryLabel = TEXT ( " Entry= " ) ;
const int32 EntryLabelLength = FCString : : Strlen ( EntryLabel ) ;
FString ConfigEntryName ;
while ( FParse : : Value ( DataPtr , EntryLabel , ConfigEntryName ) )
{
Entries . Add ( ConfigEntryName ) ;
// Skip head so we can look for any additional entries (note that we might not skip past the existing
// entry has we have no idea how much whitespace was ignored by FParse, but it will be enough)
DataPtr + = EntryLabelLength + ConfigEntryName . Len ( ) ;
}
return Entries ;
}
2020-10-21 17:56:05 -04:00
2021-06-03 04:35:17 -04:00
/**
* Profiling data allowing us to track how payloads are being push / pulled during the lifespan of the process . Note that as all backends are
* created at the same time , we don ' t need to add locked when accessing the maps . In addition FCookStats is thread safe when adding hits / misses
2021-07-28 07:27:57 -04:00
* so we don ' t have to worry about that either .
2021-06-03 04:35:17 -04:00
* We keep the FCookStats here rather than as a member of IVirtualizationBackend to try and avoid the backends needing to be aware of the data that
* we are gathering at all . This way all profiling code is kept to this cpp .
*/
namespace Profiling
{
# if ENABLE_COOK_STATS
2021-09-01 07:03:59 -04:00
TMap < FString , FCookStats : : CallStats > CacheStats ;
2021-06-03 04:35:17 -04:00
TMap < FString , FCookStats : : CallStats > PushStats ;
TMap < FString , FCookStats : : CallStats > PullStats ;
void CreateStats ( const IVirtualizationBackend & Backend )
{
2021-11-18 14:37:34 -05:00
CacheStats . Add ( Backend . GetDebugName ( ) ) ;
PushStats . Add ( Backend . GetDebugName ( ) ) ;
PullStats . Add ( Backend . GetDebugName ( ) ) ;
2021-06-03 04:35:17 -04:00
}
2021-09-01 07:03:59 -04:00
FCookStats : : CallStats & GetCacheStats ( const IVirtualizationBackend & Backend )
{
2021-11-18 14:37:34 -05:00
return * CacheStats . Find ( Backend . GetDebugName ( ) ) ;
2021-09-01 07:03:59 -04:00
}
2021-06-03 04:35:17 -04:00
FCookStats : : CallStats & GetPushStats ( const IVirtualizationBackend & Backend )
{
2021-11-18 14:37:34 -05:00
return * PushStats . Find ( Backend . GetDebugName ( ) ) ;
2021-06-03 04:35:17 -04:00
}
FCookStats : : CallStats & GetPullStats ( const IVirtualizationBackend & Backend )
{
2021-11-18 14:37:34 -05:00
return * PullStats . Find ( Backend . GetDebugName ( ) ) ;
2021-06-03 04:35:17 -04:00
}
2021-07-28 07:27:57 -04:00
/** Returns true if we have gathered any profiling data at all */
bool HasProfilingData ( )
{
auto HasAccumulatedData = [ ] ( const TMap < FString , FCookStats : : CallStats > & Stats ) - > bool
{
for ( const auto & Iterator : Stats )
{
if ( Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) > 0 )
{
return true ;
}
if ( Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Miss , FCookStats : : CallStats : : EStatType : : Counter ) > 0 )
{
return true ;
}
}
return false ;
} ;
2021-09-01 07:03:59 -04:00
return HasAccumulatedData ( CacheStats ) | | HasAccumulatedData ( PushStats ) | | HasAccumulatedData ( PullStats ) ;
2021-07-28 07:27:57 -04:00
}
2021-06-03 04:35:17 -04:00
void LogStats ( )
{
2021-07-28 07:27:57 -04:00
if ( ! HasProfilingData ( ) )
2021-06-03 04:35:17 -04:00
{
2021-07-28 07:27:57 -04:00
return ; // Early out if we have no data
2021-06-03 04:35:17 -04:00
}
2021-07-28 07:27:57 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " " ) ) ;
2021-06-03 04:35:17 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " Virtualization ProfileData " ) ) ;
2021-07-28 07:27:57 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " ======================================================================================= " ) ) ;
2021-06-03 04:35:17 -04:00
2021-09-01 07:03:59 -04:00
if ( CacheStats . Num ( ) > 0 )
{
UE_LOG ( LogVirtualization , Log , TEXT ( " %-40s|%17s|%12s|%14s| " ) , TEXT ( " Caching Data " ) , TEXT ( " TotalSize (MB) " ) , TEXT ( " TotalTime(s) " ) , TEXT ( " DataRate(MB/S) " ) ) ;
UE_LOG ( LogVirtualization , Log , TEXT ( " ----------------------------------------|-----------------|------------|--------------| " ) ) ;
for ( const auto & Iterator : CacheStats )
{
const double Time = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) * FPlatformTime : : GetSecondsPerCycle ( ) ;
const int64 DataSizeMB = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) / ( 1024 * 1024 ) ;
const double MBps = Time ! = 0.0 ? ( DataSizeMB / Time ) : 0.0 ;
UE_LOG ( LogVirtualization , Log , TEXT ( " %-40.40s|%17 " UINT64_FMT " |%12.3f|%14.3f| " ) ,
* Iterator . Key ,
DataSizeMB ,
Time ,
MBps ) ;
}
UE_LOG ( LogVirtualization , Log , TEXT ( " ======================================================================================= " ) ) ;
}
2021-06-03 04:35:17 -04:00
if ( PushStats . Num ( ) > 0 )
{
UE_LOG ( LogVirtualization , Log , TEXT ( " %-40s|%17s|%12s|%14s| " ) , TEXT ( " Pushing Data " ) , TEXT ( " TotalSize (MB) " ) , TEXT ( " TotalTime(s) " ) , TEXT ( " DataRate(MB/S) " ) ) ;
2021-07-28 07:27:57 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " ----------------------------------------|-----------------|------------|--------------| " ) ) ;
2021-06-03 04:35:17 -04:00
for ( const auto & Iterator : PushStats )
{
const double Time = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) * FPlatformTime : : GetSecondsPerCycle ( ) ;
const int64 DataSizeMB = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) / ( 1024 * 1024 ) ;
2021-06-17 13:42:38 -04:00
const double MBps = Time ! = 0.0 ? ( DataSizeMB / Time ) : 0.0 ;
2021-06-03 04:35:17 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " %-40.40s|%17 " UINT64_FMT " |%12.3f|%14.3f| " ) ,
* Iterator . Key ,
DataSizeMB ,
Time ,
2021-06-17 13:42:38 -04:00
MBps ) ;
2021-06-03 04:35:17 -04:00
}
2021-07-28 07:27:57 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " ======================================================================================= " ) ) ;
2021-06-03 04:35:17 -04:00
}
if ( PullStats . Num ( ) > 0 )
{
UE_LOG ( LogVirtualization , Log , TEXT ( " %-40s|%17s|%12s|%14s| " ) , TEXT ( " Pulling Data " ) , TEXT ( " TotalSize (MB) " ) , TEXT ( " TotalTime(s) " ) , TEXT ( " DataRate(MB/S) " ) ) ;
2021-07-28 07:27:57 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " ----------------------------------------|-----------------|------------|--------------| " ) ) ;
2021-06-03 04:35:17 -04:00
for ( const auto & Iterator : PullStats )
{
const double Time = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) * FPlatformTime : : GetSecondsPerCycle ( ) ;
const int64 DataSizeMB = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) / ( 1024 * 1024 ) ;
2021-06-17 13:42:38 -04:00
const double MBps = Time ! = 0.0 ? ( DataSizeMB / Time ) : 0.0 ;
2021-06-03 04:35:17 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " %-40.40s|%17 " UINT64_FMT " |%12.3f|%14.3f| " ) ,
* Iterator . Key ,
DataSizeMB ,
Time ,
2021-06-17 13:42:38 -04:00
MBps ) ;
2021-06-03 04:35:17 -04:00
}
2021-07-28 07:27:57 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " ======================================================================================= " ) ) ;
2021-06-03 04:35:17 -04:00
}
}
# endif // ENABLE_COOK_STATS
} //namespace Profiling
2020-10-21 17:56:05 -04:00
FVirtualizationManager : : FVirtualizationManager ( )
: bEnablePayloadPushing ( true )
2021-08-04 03:25:25 -04:00
, bEnableCacheAfterPull ( true )
2020-10-21 17:56:05 -04:00
, MinPayloadLength ( 0 )
2021-04-30 08:14:54 -04:00
, BackendGraphName ( TEXT ( " ContentVirtualizationBackendGraph_None " ) )
, bForceSingleThreaded ( false )
, bValidateAfterPushOperation ( false )
2020-10-21 17:56:05 -04:00
{
2022-02-16 01:27:09 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : FVirtualizationManager ) ;
2021-06-03 04:35:17 -04:00
// Allows us to log the profiling data on process exit.
// TODO: We should just be able to call the logging in the destructor, but
// we need to fix the startup/shutdown ordering of Mirage first.
COOK_STAT ( FCoreDelegates : : OnExit . AddStatic ( Profiling : : LogStats ) ) ;
2022-04-21 03:15:13 -04:00
DebugConsoleCommands . Add ( IConsoleManager : : Get ( ) . RegisterConsoleCommand (
TEXT ( " VA.MissBackends " ) ,
TEXT ( " A debug commnad which can be used to disable payload pulling on one or more backends " ) ,
FConsoleCommandWithArgsAndOutputDeviceDelegate : : CreateRaw ( this , & FVirtualizationManager : : OnUpdateMissBackendsFromConsole ) ) ) ;
2022-04-22 07:26:04 -04:00
DebugConsoleCommands . Add ( IConsoleManager : : Get ( ) . RegisterConsoleCommand (
TEXT ( " VA.MissChance " ) ,
TEXT ( " A debug command which can be used to set the chance that a payload pull will fail " ) ,
FConsoleCommandWithArgsAndOutputDeviceDelegate : : CreateRaw ( this , & FVirtualizationManager : : OnUpdateMissChanceFromConsole ) ) ) ;
2020-10-21 17:56:05 -04:00
}
2021-04-30 08:14:54 -04:00
FVirtualizationManager : : ~ FVirtualizationManager ( )
2020-10-21 17:56:05 -04:00
{
2021-04-30 08:14:54 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " Destroying backends " ) ) ;
2021-07-22 09:55:49 -04:00
2022-04-21 03:15:13 -04:00
for ( IConsoleCommand * Cmd : DebugConsoleCommands )
{
IConsoleManager : : Get ( ) . UnregisterConsoleObject ( Cmd ) ;
}
2021-07-22 09:55:49 -04:00
LocalCachableBackends . Empty ( ) ;
PersistentStorageBackends . Empty ( ) ;
PullEnabledBackends . Empty ( ) ;
2020-10-21 17:56:05 -04:00
2021-07-22 09:55:49 -04:00
AllBackends . Empty ( ) ; // This will delete all backends and beyond this point all references to them are invalid
2021-04-30 08:14:54 -04:00
UE_LOG ( LogVirtualization , Log , TEXT ( " Virtualization manager destroyed " ) ) ;
}
2022-03-08 11:22:45 -05:00
bool FVirtualizationManager : : Initialize ( const FInitParams & InitParams )
2022-03-07 05:42:57 -05:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : Initialize ) ;
2022-03-08 11:22:45 -05:00
ProjectName = InitParams . ProjectName ;
ApplySettingsFromConfigFiles ( InitParams . ConfigFile ) ;
ApplyDebugSettingsFromConfigFiles ( InitParams . ConfigFile ) ;
2022-03-07 05:42:57 -05:00
ApplySettingsFromCmdline ( ) ;
ApplyDebugSettingsFromFromCmdline ( ) ;
2022-03-08 11:22:45 -05:00
MountBackends ( InitParams . ConfigFile ) ;
2022-03-07 05:42:57 -05:00
return true ;
}
2021-06-03 04:35:17 -04:00
bool FVirtualizationManager : : IsEnabled ( ) const
{
2021-07-22 09:55:49 -04:00
return ! AllBackends . IsEmpty ( ) ;
2021-06-03 04:35:17 -04:00
}
2022-01-28 03:48:55 -05:00
bool FVirtualizationManager : : IsPushingEnabled ( EStorageType StorageType ) const
{
if ( ! bEnablePayloadPushing )
{
return false ;
}
switch ( StorageType )
{
case EStorageType : : Local :
return ! LocalCachableBackends . IsEmpty ( ) ;
break ;
case EStorageType : : Persistent :
return ! PersistentStorageBackends . IsEmpty ( ) ;
break ;
default :
checkNoEntry ( ) ;
return false ;
break ;
}
}
2022-02-02 02:21:24 -05:00
bool FVirtualizationManager : : PushData ( const FIoHash & Id , const FCompressedBuffer & Payload , EStorageType StorageType , const FString & Context )
2021-12-08 02:19:42 -05:00
{
FPushRequest Request ( Id , Payload , Context ) ;
return FVirtualizationManager : : PushData ( MakeArrayView ( & Request , 1 ) , StorageType ) ;
}
bool FVirtualizationManager : : PushData ( TArrayView < FPushRequest > Requests , EStorageType StorageType )
2021-04-30 08:14:54 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : PushData ) ;
2022-01-21 07:34:38 -05:00
if ( Requests . IsEmpty ( ) )
{
return true ;
}
2021-12-08 02:19:42 -05:00
TArray < FPushRequest > ValidatedRequests ;
ValidatedRequests . Reserve ( Requests . Num ( ) ) ;
TArray < int32 > OriginalToValidatedRequest ; // Builds a mapping between Requests and ValidatedRequests for later
OriginalToValidatedRequest . SetNum ( Requests . Num ( ) ) ;
// Create a new list of FPushRequest, excluding the requests that should not be processed for what ever reason.
for ( int32 Index = 0 ; Index < Requests . Num ( ) ; + + Index )
2021-04-30 08:14:54 -04:00
{
2021-12-08 02:19:42 -05:00
OriginalToValidatedRequest [ Index ] = INDEX_NONE ;
FPushRequest & Request = Requests [ Index ] ;
2022-02-02 02:21:24 -05:00
if ( Request . Identifier . IsZero ( ) | | Request . Payload . GetCompressedSize ( ) = = 0 )
2021-12-08 02:19:42 -05:00
{
Request . Status = FPushRequest : : EStatus : : Invalid ;
continue ;
}
if ( ( int64 ) Request . Payload . GetCompressedSize ( ) < MinPayloadLength )
{
2022-02-24 19:58:44 -05:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Pushing payload (id: %s) with context ('%s') was prevented as it is smaller (% " UINT64_FMT " ) than the MinPayloadLength (% " INT64_FMT " ) " ) ,
2022-01-27 04:20:46 -05:00
* LexToString ( Request . Identifier ) ,
2022-02-24 19:58:44 -05:00
* Request . Context ,
2021-12-08 02:19:42 -05:00
Request . Payload . GetCompressedSize ( ) ,
MinPayloadLength ) ;
Request . Status = FPushRequest : : EStatus : : BelowMinSize ;
continue ;
}
2022-02-16 01:27:09 -05:00
if ( ! ShouldVirtualize ( Request . Context ) )
2022-01-28 15:29:01 -05:00
{
2022-02-16 01:27:09 -05:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Pushing payload (id: %s) with context ('%s') was prevented by filtering " ) ,
2022-01-28 15:29:01 -05:00
* LexToString ( Request . Identifier ) ,
* Request . Context ) ;
Request . Status = FPushRequest : : EStatus : : ExcludedByPackagPath ;
continue ;
}
2021-12-08 02:19:42 -05:00
OriginalToValidatedRequest [ Index ] = ValidatedRequests . Num ( ) ;
ValidatedRequests . Add ( Request ) ;
}
2021-04-30 08:14:54 -04:00
2022-02-16 01:27:09 -05:00
// Early out if none of the requests require pushing after validation
if ( ValidatedRequests . IsEmpty ( ) )
{
return true ;
}
2021-04-30 08:14:54 -04:00
FConditionalScopeLock _ ( & ForceSingleThreadedCS , bForceSingleThreaded ) ;
2022-01-28 15:29:01 -05:00
// Early out if there are no backends
2021-07-22 09:55:49 -04:00
if ( ! IsEnabled ( ) | | bEnablePayloadPushing = = false )
2020-10-21 17:56:05 -04:00
{
return false ;
}
2021-04-30 08:14:54 -04:00
// TODO: Note that all push operations are currently synchronous, probably
// should change to async at some point, although this makes handling failed
// pushed much more difficult.
2021-07-22 09:55:49 -04:00
2021-10-25 20:05:28 -04:00
int32 ErrorCount = 0 ;
2021-04-30 08:14:54 -04:00
bool bWasPayloadPushed = false ;
2021-07-22 09:55:49 -04:00
FBackendArray & Backends = StorageType = = EStorageType : : Local ? LocalCachableBackends : PersistentStorageBackends ;
for ( IVirtualizationBackend * Backend : Backends )
2020-10-21 17:56:05 -04:00
{
2021-12-08 02:19:42 -05:00
const bool bResult = TryPushDataToBackend ( * Backend , ValidatedRequests ) ;
2020-10-21 17:56:05 -04:00
2021-12-08 02:19:42 -05:00
UE_CLOG ( bResult = = true , LogVirtualization , Verbose , TEXT ( " [%s] Pushed '%d' payload(s) " ) , * Backend - > GetDebugName ( ) , ValidatedRequests . Num ( ) ) ;
UE_CLOG ( bResult = = false , LogVirtualization , Error , TEXT ( " [%s] Failed to push '%d' payload(s) " ) , * Backend - > GetDebugName ( ) , ValidatedRequests . Num ( ) ) ;
2021-05-19 07:46:07 -04:00
2021-12-08 02:19:42 -05:00
if ( ! bResult )
2021-05-19 07:46:07 -04:00
{
2021-10-25 20:05:28 -04:00
ErrorCount + + ;
2021-05-19 07:46:07 -04:00
}
2021-04-30 08:14:54 -04:00
// Debugging operation where we immediately try to pull the payload after each push (when possible) and assert
// that the pulled payload is the same as the original
2022-04-21 03:15:13 -04:00
if ( bValidateAfterPushOperation & & bResult = = true & & Backend - > IsOperationSupported ( IVirtualizationBackend : : EOperations : : Pull ) )
2021-04-30 08:14:54 -04:00
{
2021-12-08 02:19:42 -05:00
for ( FPushRequest & Request : ValidatedRequests )
{
FCompressedBuffer ValidationPayload = PullDataFromBackend ( * Backend , Request . Identifier ) ;
checkf ( Request . Payload . GetRawHash ( ) = = ValidationPayload . GetRawHash ( ) ,
TEXT ( " [%s] Failed to pull payload '%s' after it was pushed to backend " ) ,
* Backend - > GetDebugName ( ) ,
2022-01-27 04:20:46 -05:00
* LexToString ( Request . Identifier ) ) ;
2021-12-08 02:19:42 -05:00
}
2021-04-30 08:14:54 -04:00
}
}
2020-10-21 17:56:05 -04:00
2021-12-08 02:19:42 -05:00
UE_CLOG ( ErrorCount = = Backends . Num ( ) , LogVirtualization , Error , TEXT ( " Failed to push '%d' payload(s) to any backend' " ) , ValidatedRequests . Num ( ) ) ;
// Now we need to update the statuses of the original list of requests with those from our validated list
for ( int32 Index = 0 ; Index < Requests . Num ( ) ; + + Index )
{
const int32 MappingIndex = OriginalToValidatedRequest [ Index ] ;
if ( MappingIndex ! = INDEX_NONE )
{
Requests [ Index ] . Status = ValidatedRequests [ MappingIndex ] . Status ;
}
}
2020-10-21 17:56:05 -04:00
2021-10-25 20:05:28 -04:00
// For local storage we consider the push to have failed only if ALL backends gave an error, if at least one backend succeeded then the operation succeeded.
// For persistent storage we require that all backends succeeded, so any errors will fail the push operation.
return StorageType = = EStorageType : : Local ? ErrorCount < Backends . Num ( ) : ErrorCount = = 0 ;
2021-04-30 08:14:54 -04:00
}
2022-02-02 02:21:24 -05:00
FCompressedBuffer FVirtualizationManager : : PullData ( const FIoHash & Id )
2021-04-30 08:14:54 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : PullData ) ;
2022-02-02 02:21:24 -05:00
if ( Id . IsZero ( ) )
2021-04-30 08:14:54 -04:00
{
// TODO: See below, should errors here be fatal?
2022-02-02 02:21:24 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Attempting to pull a virtualized payload with an invalid FIoHash " ) ) ;
2021-04-30 08:14:54 -04:00
return FCompressedBuffer ( ) ;
}
2021-07-22 09:55:49 -04:00
if ( PullEnabledBackends . IsEmpty ( ) )
2021-04-30 08:14:54 -04:00
{
// TODO: See below, should errors here be fatal?
2022-01-27 04:20:46 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Payload '%s' failed to be pulled as there are no backends mounted!' " ) , * LexToString ( Id ) ) ;
2021-04-30 08:14:54 -04:00
return FCompressedBuffer ( ) ;
}
FConditionalScopeLock _ ( & ForceSingleThreadedCS , bForceSingleThreaded ) ;
2021-11-23 15:05:13 -05:00
GetNotificationEvent ( ) . Broadcast ( IVirtualizationSystem : : PullBegunNotification , Id ) ;
2022-04-22 07:26:04 -04:00
FCompressedBuffer Payload = PullDataFromAllBackends ( Id ) ;
2021-11-23 15:05:13 -05:00
GetNotificationEvent ( ) . Broadcast ( IVirtualizationSystem : : PullEndedNotification , Id ) ;
2022-04-22 07:26:04 -04:00
if ( ! Payload . IsNull ( ) )
{
return Payload ;
}
else
{
// Broadcast the pull failed event to any listeners
GetNotificationEvent ( ) . Broadcast ( IVirtualizationSystem : : PullFailedNotification , Id ) ;
2021-11-23 15:05:13 -05:00
2022-04-22 07:26:04 -04:00
// TODO: Maybe this should be a fatal error? If we keep it as an error we need to make sure any calling
// code handles it properly.
// Could be worth extending ::PullData to return error codes instead so we can make a better distinction
// between the payload not being found in any of the backends and one or more of the backends failing.
UE_LOG ( LogVirtualization , Error , TEXT ( " Payload '%s' failed to be pulled from any backend' " ) , * LexToString ( Id ) ) ;
2021-04-30 08:14:54 -04:00
2022-04-22 07:26:04 -04:00
return FCompressedBuffer ( ) ;
}
2021-04-30 08:14:54 -04:00
}
2022-02-04 02:41:37 -05:00
EQueryResult FVirtualizationManager : : QueryPayloadStatuses ( TArrayView < const FIoHash > Ids , EStorageType StorageType , TArray < FPayloadStatus > & OutStatuses )
2021-12-01 11:13:31 -05:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : DoPayloadsExist ) ;
2022-01-21 06:49:09 -05:00
OutStatuses . SetNum ( Ids . Num ( ) ) ; // Make sure we set the number out statuses before we potentially early out
if ( Ids . IsEmpty ( ) )
{
2022-02-04 02:41:37 -05:00
return EQueryResult : : Success ;
2022-01-21 06:49:09 -05:00
}
2021-12-01 11:13:31 -05:00
for ( int32 Index = 0 ; Index < Ids . Num ( ) ; + + Index )
{
2022-02-02 02:21:24 -05:00
OutStatuses [ Index ] = Ids [ Index ] . IsZero ( ) ? FPayloadStatus : : Invalid : FPayloadStatus : : NotFound ;
2021-12-01 11:13:31 -05:00
}
FBackendArray & Backends = StorageType = = EStorageType : : Local ? LocalCachableBackends : PersistentStorageBackends ;
TArray < int8 > HitCount ;
TArray < bool > Results ;
HitCount . SetNum ( Ids . Num ( ) ) ;
Results . SetNum ( Ids . Num ( ) ) ;
{
FConditionalScopeLock _ ( & ForceSingleThreadedCS , bForceSingleThreaded ) ;
for ( IVirtualizationBackend * Backend : Backends )
{
if ( ! Backend - > DoPayloadsExist ( Ids , Results ) )
{
// If a backend entirely failed we should early out and report the problem
OutStatuses . Reset ( ) ;
2022-02-04 02:41:37 -05:00
return EQueryResult : : Failure_Unknown ;
2021-12-01 11:13:31 -05:00
}
for ( int32 Index = 0 ; Index < Ids . Num ( ) ; + + Index )
{
2022-02-02 02:21:24 -05:00
if ( ! Ids [ Index ] . IsZero ( ) & & Results [ Index ] )
2021-12-01 11:13:31 -05:00
{
HitCount [ Index ] + + ;
}
}
}
}
// Now we total up the hit count for each payload to see if it was found in none, all or some of the backends
for ( int32 Index = 0 ; Index < Ids . Num ( ) ; + + Index )
{
2022-02-02 02:21:24 -05:00
if ( ! Ids [ Index ] . IsZero ( ) )
2021-12-01 11:13:31 -05:00
{
if ( HitCount [ Index ] = = 0 )
{
OutStatuses [ Index ] = FPayloadStatus : : NotFound ;
}
else if ( HitCount [ Index ] = = Backends . Num ( ) )
{
OutStatuses [ Index ] = FPayloadStatus : : FoundAll ;
}
else
{
2022-02-04 02:41:37 -05:00
OutStatuses [ Index ] = FPayloadStatus : : FoundPartial ;
2021-12-01 11:13:31 -05:00
}
}
}
2022-02-04 02:41:37 -05:00
return EQueryResult : : Success ;
2021-12-01 11:13:31 -05:00
}
2022-03-23 11:14:47 -04:00
bool FVirtualizationManager : : TryVirtualizePackages ( const TArray < FString > & FilesToVirtualize , TArray < FText > & OutDescriptionTags , TArray < FText > & OutErrors )
{
OutDescriptionTags . Reset ( ) ;
OutErrors . Reset ( ) ;
UE : : Virtualization : : VirtualizePackages ( FilesToVirtualize , OutDescriptionTags , OutErrors ) ;
return OutErrors . IsEmpty ( ) ;
}
2021-11-18 14:37:34 -05:00
FPayloadActivityInfo FVirtualizationManager : : GetAccumualtedPayloadActivityInfo ( ) const
2021-06-03 04:35:17 -04:00
{
FPayloadActivityInfo Info ;
# if ENABLE_COOK_STATS
2021-09-01 07:03:59 -04:00
for ( const auto & Iterator : Profiling : : CacheStats )
{
Info . Cache . PayloadCount + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
Info . Cache . TotalBytes + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) ;
Info . Cache . CyclesSpent + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) ;
}
2021-06-03 04:35:17 -04:00
for ( const auto & Iterator : Profiling : : PushStats )
{
2021-11-18 14:37:34 -05:00
Info . Push . PayloadCount + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
Info . Push . TotalBytes + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) ;
Info . Push . CyclesSpent + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) ;
2021-06-03 04:35:17 -04:00
}
for ( const auto & Iterator : Profiling : : PullStats )
{
2021-07-21 04:01:37 -04:00
Info . Pull . PayloadCount + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
Info . Pull . TotalBytes + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) ;
Info . Pull . CyclesSpent + = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) ;
2021-06-03 04:35:17 -04:00
}
# endif // ENABLE_COOK_STATS
return Info ;
}
2021-11-18 14:37:34 -05:00
void FVirtualizationManager : : GetPayloadActivityInfo ( GetPayloadActivityInfoFuncRef GetPayloadFunc ) const
{
FPayloadActivityInfo Info ;
# if ENABLE_COOK_STATS
for ( const auto & Backend : AllBackends )
{
const FCookStats : : CallStats & CacheStats = Profiling : : GetCacheStats ( * Backend ) ;
Info . Cache . PayloadCount = CacheStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
Info . Cache . TotalBytes = CacheStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) ;
Info . Cache . CyclesSpent = CacheStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) ;
const FCookStats : : CallStats & PushStats = Profiling : : GetPushStats ( * Backend ) ;
Info . Push . PayloadCount = PushStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
Info . Push . TotalBytes = PushStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) ;
Info . Push . CyclesSpent = PushStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) ;
const FCookStats : : CallStats & PullStats = Profiling : : GetPullStats ( * Backend ) ;
Info . Pull . PayloadCount = PullStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
Info . Pull . TotalBytes = PullStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) ;
Info . Pull . CyclesSpent = PullStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) ;
GetPayloadFunc ( Backend - > GetDebugName ( ) , Backend - > GetConfigName ( ) , Info ) ;
}
# endif // ENABLE_COOK_STATS
}
2022-03-07 05:42:57 -05:00
void FVirtualizationManager : : ApplySettingsFromConfigFiles ( const FConfigFile & ConfigFile )
2021-04-30 08:14:54 -04:00
{
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Loading virtualization manager settings from config files... " ) ) ;
2021-04-30 08:14:54 -04:00
bool bEnablePayloadPushingFromIni = false ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetBool ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " EnablePushToBackend " ) , bEnablePayloadPushingFromIni ) )
2021-04-30 08:14:54 -04:00
{
bEnablePayloadPushing = bEnablePayloadPushingFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t EnablePushToBackend : %s " ) , bEnablePayloadPushing ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-04-30 08:14:54 -04:00
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization].EnablePushToBackend from config file! " ) ) ;
}
2021-08-04 03:25:25 -04:00
bool bEnableCacheAfterPullFromIni = false ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetBool ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " EnableCacheAfterPull " ) , bEnableCacheAfterPullFromIni ) )
2021-08-04 03:25:25 -04:00
{
bEnableCacheAfterPull = bEnableCacheAfterPullFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t CachePulledPayloads : %s " ) , bEnableCacheAfterPull ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-08-04 03:25:25 -04:00
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization].EnableCacheAfterPull from config file! " ) ) ;
}
2021-04-30 08:14:54 -04:00
int64 MinPayloadLengthFromIni = 0 ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetInt64 ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " MinPayloadLength " ) , MinPayloadLengthFromIni ) )
2021-04-30 08:14:54 -04:00
{
MinPayloadLength = MinPayloadLengthFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t MinPayloadLength : % " INT64_FMT ) , MinPayloadLength ) ;
2021-04-30 08:14:54 -04:00
}
else
{
2022-02-16 01:27:09 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization].MinPayloadLength from config file! " ) ) ;
2021-04-30 08:14:54 -04:00
}
FString BackendGraphNameFromIni ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetString ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " BackendGraph " ) , BackendGraphNameFromIni ) )
2021-04-30 08:14:54 -04:00
{
BackendGraphName = BackendGraphNameFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t BackendGraphName : %s " ) , * BackendGraphName ) ;
2021-04-30 08:14:54 -04:00
}
else
{
2022-02-16 01:27:09 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization].BackendGraph from config file! " ) ) ;
}
FString FilterModeFromIni ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetString ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " FilterMode " ) , FilterModeFromIni ) )
2022-02-16 01:27:09 -05:00
{
if ( LexTryParseString ( FilteringMode , FilterModeFromIni ) )
{
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t FilterMode : %s " ) , * FilterModeFromIni ) ;
2022-02-16 01:27:09 -05:00
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " [Core.ContentVirtualization].FilterMode was an invalid value! Allowed: 'OptIn'|'OptOut' Found '%s' " ) , * FilterModeFromIni ) ;
}
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization]FilterMode from config file! " ) ) ;
2021-04-30 08:14:54 -04:00
}
2021-11-07 23:43:01 -05:00
bool bFilterEngineContentFromIni = true ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetBool ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " FilterEngineContent " ) , bFilterEngineContentFromIni ) )
2021-11-07 23:43:01 -05:00
{
bFilterEngineContent = bFilterEngineContentFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t FilterEngineContent : %s " ) , bFilterEngineContent ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-11-07 23:43:01 -05:00
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization].FilterEngineContent from config file! " ) ) ;
}
bool bFilterEnginePluginContentFromIni = true ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetBool ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " FilterEnginePluginContent " ) , bFilterEnginePluginContentFromIni ) )
2021-11-07 23:43:01 -05:00
{
bFilterEnginePluginContent = bFilterEnginePluginContentFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t FilterEnginePluginContent : %s " ) , bFilterEnginePluginContent ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-11-07 23:43:01 -05:00
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to load [Core.ContentVirtualization].FilterEnginePluginContent from config file! " ) ) ;
}
2021-04-30 08:14:54 -04:00
}
void FVirtualizationManager : : ApplySettingsFromCmdline ( )
{
FString CmdlineGraphName ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -BackendGraph= " ) , CmdlineGraphName ) )
{
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Backend graph overriden from the cmdline: '%s' " ) , * CmdlineGraphName ) ;
2021-04-30 08:14:54 -04:00
BackendGraphName = CmdlineGraphName ;
}
if ( FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " VirtualizationForceSingleThreaded " ) ) )
{
bForceSingleThreaded = true ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " ForceSingleThreaded overriden from the cmdline: true " ) ) ;
2021-04-30 08:14:54 -04:00
}
}
2022-03-07 05:42:57 -05:00
void FVirtualizationManager : : ApplyDebugSettingsFromConfigFiles ( const FConfigFile & ConfigFile )
2021-04-30 08:14:54 -04:00
{
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Loading virtualization manager debugging settings from config files... " ) ) ;
2021-04-30 08:14:54 -04:00
// Note that the debug settings are optional and could be left out of the config files entirely
bool bForceSingleThreadedFromIni = false ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetBool ( TEXT ( " Core.ContentVirtualizationDebugOptions " ) , TEXT ( " ForceSingleThreaded " ) , bForceSingleThreadedFromIni ) )
2021-04-30 08:14:54 -04:00
{
bForceSingleThreaded = bForceSingleThreadedFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t ForceSingleThreaded : %s " ) , bForceSingleThreaded ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-04-30 08:14:54 -04:00
}
bool bValidateAfterPushOperationFromIni = false ;
2022-03-07 05:42:57 -05:00
if ( ConfigFile . GetBool ( TEXT ( " Core.ContentVirtualizationDebugOptions " ) , TEXT ( " ValidateAfterPushOperation " ) , bValidateAfterPushOperationFromIni ) )
2021-04-30 08:14:54 -04:00
{
bValidateAfterPushOperation = bValidateAfterPushOperationFromIni ;
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t ValidateAfterPushOperation : %s " ) , bValidateAfterPushOperation ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-04-30 08:14:54 -04:00
}
// Some debug options will cause intentional breaks or slow downs for testing purposes, if these are enabled then we should give warning/errors
// so it is clear in the log that future failures are being caused by the given dev option.
UE_CLOG ( bForceSingleThreaded , LogVirtualization , Warning , TEXT ( " ForceSingleThreaded is enabled, virtualization will run in single threaded mode and may be slower! " ) ) ;
UE_CLOG ( bValidateAfterPushOperation , LogVirtualization , Error , TEXT ( " ValidateAfterPushOperation is enabled, each push will be followed by a pull to validate it! " ) ) ;
}
2021-11-18 14:37:34 -05:00
void FVirtualizationManager : : ApplyDebugSettingsFromFromCmdline ( )
{
FString MissOptions ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -VA-MissBackends= " ) , MissOptions ) )
{
2022-04-21 03:15:13 -04:00
MissOptions . ParseIntoArray ( DebugMissBackends , TEXT ( " + " ) , true ) ;
2021-11-18 14:37:34 -05:00
UE_LOG ( LogVirtualization , Warning , TEXT ( " Cmdline has disabled payload pulling for the following backends: " ) ) ;
2022-04-21 03:15:13 -04:00
for ( const FString & Backend : DebugMissBackends )
2021-11-18 14:37:34 -05:00
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t %s " ) , * Backend ) ;
}
}
2022-04-22 07:26:04 -04:00
DebugMissChance = 0.0f ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -VA-MissChance= " ) , DebugMissChance ) )
{
DebugMissChance = FMath : : Clamp ( DebugMissChance , 0.0f , 100.0f ) ;
UE_LOG ( LogVirtualization , Warning , TEXT ( " Cmdline has set a %.1f%% chance of a payload pull failing " ) , DebugMissChance ) ;
}
2021-11-18 14:37:34 -05:00
}
2022-04-21 03:15:13 -04:00
void FVirtualizationManager : : OnUpdateMissBackendsFromConsole ( const TArray < FString > & Args , FOutputDevice & OutputDevice )
{
if ( Args . IsEmpty ( ) )
{
OutputDevice . Log ( TEXT ( " VA.MissBackends command help " ) ) ;
OutputDevice . Log ( TEXT ( " This command allows you to disable the pulling of payloads by specific backends " ) ) ;
OutputDevice . Log ( TEXT ( " " ) ) ;
OutputDevice . Log ( TEXT ( " Commands: " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissBackends reset - Empties the list of backends, everything will function normally " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissBackends list - Prints the list of backends affected " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissBackends set Name0 Name1 - List each backend that you want to fail to pull payloads " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissBackends set All - All backends will fail to pull payloads " ) ) ;
OutputDevice . Log ( TEXT ( " " ) ) ;
OutputDevice . Log ( TEXT ( " Valid backend names: " ) ) ;
for ( const TUniquePtr < IVirtualizationBackend > & Backend : AllBackends )
{
OutputDevice . Logf ( TEXT ( " \t %s " ) , * Backend - > GetConfigName ( ) ) ;
}
}
else if ( Args . Num ( ) = = 1 )
{
if ( Args [ 0 ] = = TEXT ( " reset " ) )
{
DebugMissBackends . Empty ( ) ;
UpdateBackendDebugState ( ) ;
}
else if ( Args [ 0 ] = = TEXT ( " list " ) )
{
if ( ! DebugMissBackends . IsEmpty ( ) )
{
OutputDevice . Log ( TEXT ( " Disabled backends: " ) ) ;
for ( const FString & Backend : DebugMissBackends )
{
OutputDevice . Logf ( TEXT ( " \t %s " ) , * Backend ) ;
}
}
else
{
OutputDevice . Log ( TEXT ( " No backends are disabled " ) ) ;
}
}
else
{
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid args for the VA.MissBackends command! " ) ) ;
}
}
else if ( Args [ 0 ] = = TEXT ( " set " ) )
{
DebugMissBackends . Empty ( Args . Num ( ) - 1 ) ;
for ( int32 Index = 1 ; Index < Args . Num ( ) ; + + Index )
{
DebugMissBackends . Add ( Args [ Index ] ) ;
}
UpdateBackendDebugState ( ) ;
}
else
{
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid args for the VA.MissBackends command! " ) ) ;
}
}
2022-04-22 07:26:04 -04:00
void FVirtualizationManager : : OnUpdateMissChanceFromConsole ( const TArray < FString > & Args , FOutputDevice & OutputDevice )
{
if ( Args . IsEmpty ( ) )
{
OutputDevice . Log ( TEXT ( " VA.MissChance command help " ) ) ;
OutputDevice . Log ( TEXT ( " This command allows you to set the chance (in percent) that a payload pull request will just fail " ) ) ;
OutputDevice . Log ( TEXT ( " " ) ) ;
OutputDevice . Log ( TEXT ( " Commands: " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissChance show - prints the current miss percent chance " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissChance set Num - Sets the miss percent chance to the given value " ) ) ;
}
else if ( Args . Num ( ) = = 1 & & Args [ 0 ] = = TEXT ( " show " ) )
{
OutputDevice . Logf ( TEXT ( " Current debug miss chance: %.1f%% " ) , DebugMissChance ) ;
}
else if ( Args . Num ( ) = = 2 & & Args [ 0 ] = = TEXT ( " set " ) )
{
if ( : : LexTryParseString ( DebugMissChance , * Args [ 1 ] ) )
{
DebugMissChance = FMath : : Clamp ( DebugMissChance , 0.0f , 100.0f ) ;
OutputDevice . Logf ( TEXT ( " Current debug miss chance set to %.1f%% " ) , DebugMissChance ) ;
}
else
{
DebugMissChance = 0.0f ;
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid value, current debug miss chance reset to 0.0% " ) ) ;
}
}
else
{
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid args for the VA.MissChance command! " ) ) ;
}
}
2022-04-21 03:15:13 -04:00
void FVirtualizationManager : : UpdateBackendDebugState ( )
{
for ( TUniquePtr < IVirtualizationBackend > & Backend : AllBackends )
{
const bool bDisable = ShouldDebugDisablePulling ( Backend - > GetConfigName ( ) ) ;
Backend - > SetOperationDebugState ( IVirtualizationBackend : : EOperations : : Pull , bDisable ) ;
}
}
bool FVirtualizationManager : : ShouldDebugDisablePulling ( FStringView BackendConfigName ) const
{
if ( DebugMissBackends . IsEmpty ( ) )
{
return false ;
}
if ( DebugMissBackends [ 0 ] = = TEXT ( " All " ) )
{
return true ;
}
for ( const FString & Name : DebugMissBackends )
{
if ( Name = = BackendConfigName )
{
return true ;
}
}
return false ;
}
2022-04-22 07:26:04 -04:00
bool FVirtualizationManager : : ShouldDebugFailPulling ( ) const
{
if ( DebugMissChance = = 0.0f )
{
return false ;
}
else
{
// Could consider adding a lock here, although FRandomStream
// is thread safe, many threads hitting it could cause a few
// threads to get the same results.
// Since this is a debug function and the percent is only a
// rough guide, adding a lock is considered overkill. This
// should only be done if in the future we decide that we want
// more accuracy.
static FRandomStream RandomStream ( NAME_None ) ;
const float RandValue = RandomStream . FRand ( ) * 100.0f ;
return RandValue < = DebugMissChance ;
}
}
2022-03-07 05:42:57 -05:00
void FVirtualizationManager : : MountBackends ( const FConfigFile & ConfigFile )
2021-04-30 08:14:54 -04:00
{
2022-02-16 01:27:09 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : MountBackends ) ;
2021-07-22 09:55:49 -04:00
const FRegistedFactories FactoryLookupTable = FindBackendFactories ( ) ;
2021-04-30 08:14:54 -04:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Found %d backend factories " ) , FactoryLookupTable . Num ( ) ) ;
const TCHAR * GraphName = * BackendGraphName ;
2022-03-07 05:42:57 -05:00
if ( ! ConfigFile . DoesSectionExist ( GraphName ) )
2021-09-02 07:43:20 -04:00
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " Unable to find the backend graph: '%s' [ini=%s]. " ) , GraphName , * GEngineIni ) ;
}
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Mounting virtualization backend graph: '%s' " ) , GraphName ) ;
2021-04-30 08:14:54 -04:00
2021-07-22 09:55:49 -04:00
// It is important to parse the local storage hierarchy first so those backends will show up before the
// persistent storage backends in 'PullEnabledBackends'.
2022-03-07 05:42:57 -05:00
ParseHierarchy ( ConfigFile , GraphName , TEXT ( " LocalStorageHierarchy " ) , FactoryLookupTable , LocalCachableBackends ) ;
ParseHierarchy ( ConfigFile , GraphName , TEXT ( " PersistentStorageHierarchy " ) , FactoryLookupTable , PersistentStorageBackends ) ;
2022-04-21 03:15:13 -04:00
// Apply and disabled backends from the command line
UpdateBackendDebugState ( ) ;
2021-07-22 09:55:49 -04:00
}
2022-03-07 05:42:57 -05:00
void FVirtualizationManager : : ParseHierarchy ( const FConfigFile & ConfigFile , const TCHAR * GraphName , const TCHAR * HierarchyKey , const FRegistedFactories & FactoryLookupTable , FBackendArray & PushArray )
2021-07-22 09:55:49 -04:00
{
2021-04-30 08:14:54 -04:00
FString HierarchyData ;
2022-03-07 05:42:57 -05:00
if ( ! ConfigFile . GetValue ( GraphName , HierarchyKey , HierarchyData ) )
2021-04-30 08:14:54 -04:00
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " Unable to find the '%s' entry for the content virtualization backend graph '%s' [ini=%s]. " ) , HierarchyKey , GraphName , * GEngineIni ) ;
}
if ( HierarchyData . IsEmpty ( ) )
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " The '%s' entry for backend graph '%s' is empty [ini=%s]. " ) , HierarchyKey , GraphName , * GEngineIni ) ;
}
2022-01-25 04:29:34 -05:00
const TArray < FString > Entries = ParseEntries ( HierarchyData ) ;
2021-04-30 08:14:54 -04:00
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " '%s' has %d backend(s) " ) , HierarchyKey , Entries . Num ( ) ) ;
2021-04-30 08:14:54 -04:00
for ( const FString & Entry : Entries )
{
2022-03-07 05:42:57 -05:00
CreateBackend ( ConfigFile , GraphName , Entry , FactoryLookupTable , PushArray ) ;
2021-04-30 08:14:54 -04:00
}
}
2022-03-07 05:42:57 -05:00
bool FVirtualizationManager : : CreateBackend ( const FConfigFile & ConfigFile , const TCHAR * GraphName , const FString & ConfigEntryName , const FRegistedFactories & FactoryLookupTable , FBackendArray & PushArray )
2021-04-30 08:14:54 -04:00
{
// All failures in this method are considered fatal, however it still returns true/false in case we decide
// to be more forgiving in the future.
2022-02-17 02:53:27 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Mounting backend entry '%s' " ) , * ConfigEntryName ) ;
2021-04-30 08:14:54 -04:00
FString BackendData ;
2022-03-07 05:42:57 -05:00
if ( ! ConfigFile . GetValue ( GraphName , * ConfigEntryName , BackendData ) )
2021-04-30 08:14:54 -04:00
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " Unable to find the entry '%s' in the content virtualization backend graph '%s' [ini=%s]. " ) , * ConfigEntryName , GraphName , * GEngineIni ) ;
return false ;
}
FString BackendType ;
if ( FParse : : Value ( * BackendData , TEXT ( " Type= " ) , BackendType ) & & ! BackendType . IsEmpty ( ) )
{
// Put the rest of the ini file entry into a string to pass to the backend.
FString Cmdine = BackendData . RightChop ( BackendData . Find ( BackendType ) + BackendType . Len ( ) ) ;
Cmdine . RemoveFromEnd ( TEXT ( " ) " ) ) ;
2021-07-22 09:55:49 -04:00
UE : : Virtualization : : IVirtualizationBackendFactory * const * FactoryPtr = FactoryLookupTable . Find ( FName ( BackendType ) ) ;
2021-04-30 08:14:54 -04:00
if ( FactoryPtr ! = nullptr & & * FactoryPtr ! = nullptr )
{
IVirtualizationBackendFactory * Factory = * FactoryPtr ;
2022-03-08 11:22:45 -05:00
TUniquePtr < IVirtualizationBackend > Backend = Factory - > CreateInstance ( ProjectName , ConfigEntryName ) ;
2021-04-30 08:14:54 -04:00
if ( Backend = = nullptr )
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " IVirtualizationBackendFactory '%s' failed to create an instance! " ) , * Factory - > GetName ( ) . ToString ( ) ) ;
return false ;
}
2021-11-18 14:37:34 -05:00
2021-04-30 08:14:54 -04:00
if ( Backend - > Initialize ( Cmdine ) )
{
2021-07-22 09:55:49 -04:00
AddBackend ( MoveTemp ( Backend ) , PushArray ) ;
2021-04-30 08:14:54 -04:00
}
else
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " Backend '%s' reported errors when initializing " ) , * ConfigEntryName ) ;
return false ;
}
}
else
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " No backend factory found that can create the type '%s' " ) , * BackendType ) ;
return false ;
}
}
else
{
UE_LOG ( LogVirtualization , Fatal , TEXT ( " No 'Type=' entry found for '%s' in the config file " ) , * ConfigEntryName ) ;
return false ;
2020-10-21 17:56:05 -04:00
}
return true ;
}
2021-07-22 09:55:49 -04:00
void FVirtualizationManager : : AddBackend ( TUniquePtr < IVirtualizationBackend > Backend , FBackendArray & PushArray )
2020-10-21 17:56:05 -04:00
{
2021-11-18 14:37:34 -05:00
checkf ( ! AllBackends . Contains ( Backend ) , TEXT ( " Adding the same virtualization backend (%s) multiple times! " ) , * Backend - > GetDebugName ( ) ) ;
2020-10-21 17:56:05 -04:00
2021-07-22 09:55:49 -04:00
// Move ownership of the backend to AllBackends
AllBackends . Add ( MoveTemp ( Backend ) ) ;
2020-10-21 17:56:05 -04:00
2021-07-22 09:55:49 -04:00
// Get a reference pointer to use in the other backend arrays
IVirtualizationBackend * BackendRef = AllBackends . Last ( ) . Get ( ) ;
2022-04-21 03:15:13 -04:00
if ( BackendRef - > IsOperationSupported ( IVirtualizationBackend : : EOperations : : Pull ) )
2020-10-21 17:56:05 -04:00
{
2021-07-22 09:55:49 -04:00
PullEnabledBackends . Add ( BackendRef ) ;
2020-10-21 17:56:05 -04:00
}
2021-04-30 08:14:54 -04:00
2022-04-21 03:15:13 -04:00
if ( BackendRef - > IsOperationSupported ( IVirtualizationBackend : : EOperations : : Push ) )
2020-10-21 17:56:05 -04:00
{
2021-07-22 09:55:49 -04:00
PushArray . Add ( BackendRef ) ;
2020-10-21 17:56:05 -04:00
}
2021-04-30 08:14:54 -04:00
2021-07-22 09:55:49 -04:00
COOK_STAT ( Profiling : : CreateStats ( * BackendRef ) ) ;
}
2022-02-02 02:21:24 -05:00
void FVirtualizationManager : : CachePayload ( const FIoHash & Id , const FCompressedBuffer & Payload , const IVirtualizationBackend * BackendSource )
2021-07-22 09:55:49 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : CachePayload ) ;
// We start caching at the first (assumed to be fastest) local cache backend.
for ( IVirtualizationBackend * BackendToCache : LocalCachableBackends )
{
if ( BackendToCache = = BackendSource )
{
return ; // No point going past BackendSource
}
2022-01-25 04:29:34 -05:00
const bool bResult = TryCacheDataToBackend ( * BackendToCache , Id , Payload ) ;
2021-09-01 07:03:59 -04:00
UE_CLOG ( ! bResult , LogVirtualization , Warning ,
TEXT ( " Failed to cache payload '%s' to backend '%s' " ) ,
2022-01-27 04:20:46 -05:00
* LexToString ( Id ) ,
2021-11-18 14:37:34 -05:00
* BackendToCache - > GetDebugName ( ) ) ;
2021-09-01 07:03:59 -04:00
// Debugging operation where we immediately try to pull the payload after each push (when possible) and assert
// that the pulled payload is the same as the original
2022-04-21 03:15:13 -04:00
if ( bValidateAfterPushOperation & & bResult & & BackendToCache - > IsOperationSupported ( IVirtualizationBackend : : EOperations : : Pull ) )
2021-09-01 07:03:59 -04:00
{
FCompressedBuffer PulledPayload = PullDataFromBackend ( * BackendToCache , Id ) ;
2021-11-18 14:37:34 -05:00
checkf ( Payload . GetRawHash ( ) = = PulledPayload . GetRawHash ( ) ,
TEXT ( " [%s] Failed to pull payload '%s' after it was cached to backend " ) ,
* BackendToCache - > GetDebugName ( ) ,
2022-01-27 04:20:46 -05:00
* LexToString ( Id ) ) ;
2021-09-01 07:03:59 -04:00
}
2021-07-22 09:55:49 -04:00
}
2020-10-21 17:56:05 -04:00
}
2022-02-02 02:21:24 -05:00
bool FVirtualizationManager : : TryCacheDataToBackend ( IVirtualizationBackend & Backend , const FIoHash & Id , const FCompressedBuffer & Payload )
2021-09-01 07:03:59 -04:00
{
COOK_STAT ( FCookStats : : FScopedStatsCounter Timer ( Profiling : : GetCacheStats ( Backend ) ) ) ;
2021-12-08 02:19:42 -05:00
const EPushResult Result = Backend . PushData ( Id , Payload , FString ( ) ) ;
2021-09-01 07:03:59 -04:00
if ( Result = = EPushResult : : Success )
{
COOK_STAT ( Timer . AddHit ( Payload . GetCompressedSize ( ) ) ) ;
}
return Result ! = EPushResult : : Failed ;
}
2021-12-08 02:19:42 -05:00
bool FVirtualizationManager : : TryPushDataToBackend ( IVirtualizationBackend & Backend , TArrayView < FPushRequest > Requests )
2021-06-03 04:35:17 -04:00
{
2022-01-25 04:29:34 -05:00
COOK_STAT ( FCookStats : : CallStats & Stats = Profiling : : GetPushStats ( Backend ) ) ;
COOK_STAT ( FCookStats : : FScopedStatsCounter Timer ( Stats ) ) ;
COOK_STAT ( Timer . TrackCyclesOnly ( ) ) ;
2021-06-03 04:35:17 -04:00
2022-01-25 04:29:34 -05:00
const bool bPushResult = Backend . PushData ( Requests ) ;
# if ENABLE_COOK_STATS
if ( bPushResult )
2021-06-03 04:35:17 -04:00
{
2022-01-25 04:29:34 -05:00
Timer . AddHit ( 0 ) ;
const bool bIsInGameThread = IsInGameThread ( ) ;
for ( const FPushRequest & Request : Requests )
2021-12-08 02:19:42 -05:00
{
// TODO: Don't add a hit if the payload was already uploaded
if ( Request . Status = = FPushRequest : : EStatus : : Success )
{
2022-01-25 04:29:34 -05:00
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ;
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes , Request . Payload . GetCompressedSize ( ) , bIsInGameThread ) ;
2021-12-08 02:19:42 -05:00
}
}
2021-06-03 04:35:17 -04:00
}
2022-01-25 04:29:34 -05:00
# endif // ENABLE_COOK_STATS
2021-12-08 02:19:42 -05:00
2022-01-25 04:29:34 -05:00
return bPushResult ;
2021-06-03 04:35:17 -04:00
}
2022-04-22 07:26:04 -04:00
FCompressedBuffer FVirtualizationManager : : PullDataFromAllBackends ( const FIoHash & Id )
{
if ( ShouldDebugFailPulling ( ) )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Debug miss chance (%.1f%%) invoked when pulling payload '%s' " ) , DebugMissChance , * LexToString ( Id ) ) ;
return FCompressedBuffer ( ) ;
}
for ( IVirtualizationBackend * Backend : PullEnabledBackends )
{
// Skip if pulling has been disabled on this backend for debug purposes
if ( Backend - > IsOperationDebugDisabled ( IVirtualizationBackend : : EOperations : : Pull ) )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Pulling from backend '%s' is debug disabled for payload '%s' " ) , * Backend - > GetDebugName ( ) , * LexToString ( Id ) ) ;
continue ;
}
FCompressedBuffer Payload = PullDataFromBackend ( * Backend , Id ) ;
if ( Payload )
{
if ( bEnableCacheAfterPull )
{
CachePayload ( Id , Payload , Backend ) ;
}
return Payload ;
}
}
return FCompressedBuffer ( ) ;
}
2022-02-02 02:21:24 -05:00
FCompressedBuffer FVirtualizationManager : : PullDataFromBackend ( IVirtualizationBackend & Backend , const FIoHash & Id )
2021-06-03 04:35:17 -04:00
{
COOK_STAT ( FCookStats : : FScopedStatsCounter Timer ( Profiling : : GetPullStats ( Backend ) ) ) ;
FCompressedBuffer Payload = Backend . PullData ( Id ) ;
if ( ! Payload . IsNull ( ) )
{
COOK_STAT ( Timer . AddHit ( Payload . GetCompressedSize ( ) ) ) ;
}
return Payload ;
}
2021-11-07 23:43:01 -05:00
bool FVirtualizationManager : : ShouldVirtualizePackage ( const FPackagePath & PackagePath ) const
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : ShouldVirtualizePackage ) ;
// We require a valid mounted path for filtering
if ( ! PackagePath . IsMountedPath ( ) )
{
return true ;
}
TStringBuilder < 256 > PackageName ;
PackagePath . AppendPackageName ( PackageName ) ;
TStringBuilder < 64 > MountPointName ;
TStringBuilder < 256 > MountPointPath ;
TStringBuilder < 256 > RelativePath ;
if ( ! FPackageName : : TryGetMountPointForPath ( PackageName , MountPointName , MountPointPath , RelativePath ) )
{
return true ;
}
if ( bFilterEngineContent )
{
// Do not virtualize engine content
if ( MountPointName . ToView ( ) = = TEXT ( " /Engine/ " ) )
{
return false ;
}
}
if ( bFilterEnginePluginContent )
{
// Do not virtualize engine plugin content
if ( FPaths : : IsUnderDirectory ( MountPointPath . ToString ( ) , FPaths : : EnginePluginsDir ( ) ) )
{
return false ;
}
}
const UVirtualizationFilterSettings * Settings = GetDefault < UVirtualizationFilterSettings > ( ) ;
if ( Settings ! = nullptr )
{
2022-02-16 01:27:09 -05:00
auto DoesMatch = [ ] ( const TArray < FString > & Paths , const FStringView & PackagePath ) - > bool
2021-11-07 23:43:01 -05:00
{
2022-02-16 01:27:09 -05:00
for ( const FString & PathToMatch : Paths )
2021-11-07 23:43:01 -05:00
{
2022-02-16 01:27:09 -05:00
if ( PathToMatch . EndsWith ( TEXT ( " / " ) ) )
2021-11-07 23:43:01 -05:00
{
2022-02-16 01:27:09 -05:00
// Directory path, exclude everything under it
if ( PackagePath . StartsWith ( PathToMatch ) )
{
return true ;
}
}
else
{
// Path to an asset, exclude if it matches exactly
if ( PackagePath = = PathToMatch )
{
return true ;
}
2021-11-07 23:43:01 -05:00
}
}
2022-02-16 01:27:09 -05:00
return false ;
} ;
const FStringView PackageNameView = PackageName . ToView ( ) ;
if ( DoesMatch ( Settings - > ExcludePackagePaths , PackageNameView ) )
{
return false ;
}
if ( DoesMatch ( Settings - > IncludePackagePaths , PackageNameView ) )
{
return true ;
2021-11-07 23:43:01 -05:00
}
}
2022-02-16 01:27:09 -05:00
// The package is not in any of the include/exclude paths so we use the default behavior
return ShouldVirtualizeAsDefault ( ) ;
2021-11-07 23:43:01 -05:00
}
2022-02-16 01:27:09 -05:00
bool FVirtualizationManager : : ShouldVirtualize ( const FString & Context ) const
2022-01-28 15:29:01 -05:00
{
2022-02-16 01:27:09 -05:00
// First see if we can convert the context from a raw string to a valid package path.
// If we can extract a package path then we should use the package filtering code
// path instead.
2022-01-28 15:29:01 -05:00
FPackagePath PackagePath ;
if ( FPackagePath : : TryFromPackageName ( Context , PackagePath ) )
{
return ShouldVirtualizePackage ( PackagePath ) ;
}
if ( FPackagePath : : TryFromMountedName ( Context , PackagePath ) )
{
return ShouldVirtualizePackage ( PackagePath ) ;
}
2022-02-16 01:27:09 -05:00
// The package is not in any of the include/exclude paths so we use the default behavior
return ShouldVirtualizeAsDefault ( ) ;
}
bool FVirtualizationManager : : ShouldVirtualizeAsDefault ( ) const
{
switch ( FilteringMode )
{
case EPackageFilterMode : : OptOut :
return true ;
case EPackageFilterMode : : OptIn :
return false ;
default :
checkNoEntry ( ) ;
return false ;
}
2022-01-28 15:29:01 -05:00
}
2021-04-30 08:14:54 -04:00
} // namespace UE::Virtualization
2022-04-22 08:55:44 -04:00
# undef LOCTEXT_NAMESPACE