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 " ) )
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 ) ) ;
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 " ) ) ;
2022-04-25 08:14:36 -04:00
for ( IConsoleObject * ConsoleObject : DebugValues . ConsoleObjects )
2022-04-21 03:15:13 -04:00
{
2022-04-25 08:14:36 -04:00
IConsoleManager : : Get ( ) . UnregisterConsoleObject ( ConsoleObject ) ;
2022-04-21 03:15:13 -04:00
}
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 ) ;
2022-03-07 05:42:57 -05:00
ApplyDebugSettingsFromFromCmdline ( ) ;
2022-04-25 08:14:36 -04:00
// Do this after all of the command line settings have been processed and any
// requested debug value changes already set.
RegisterConsoleCommands ( ) ;
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-05-17 07:54:28 -04:00
bool FVirtualizationManager : : IsDisabledForObject ( const UObject * OwnerObject ) const
{
if ( OwnerObject = = nullptr )
{
return false ;
}
const UClass * OwnerClass = OwnerObject - > GetClass ( ) ;
if ( OwnerClass = = nullptr )
{
// TODO: Not actually sure if the class being nullptr is reasonable or if we should warn/error here?
return false ;
}
FName ClassName = OwnerClass - > GetFName ( ) ;
return DisabledAssetTypes . Find ( ClassName ) ! = nullptr ;
}
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-05-04 08:58:50 -04:00
if ( Request . GetIdentifier ( ) . IsZero ( ) | | Request . GetPayloadSize ( ) = = 0 )
2021-12-08 02:19:42 -05:00
{
2022-05-04 08:58:50 -04:00
Request . SetStatus ( FPushRequest : : EStatus : : Invalid ) ;
2021-12-08 02:19:42 -05:00
continue ;
}
2022-05-04 08:58:50 -04:00
if ( ( int64 ) Request . GetPayloadSize ( ) < MinPayloadLength )
2021-12-08 02:19:42 -05:00
{
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-05-04 08:58:50 -04:00
* LexToString ( Request . GetIdentifier ( ) ) ,
* Request . GetContext ( ) ,
Request . GetPayloadSize ( ) ,
2021-12-08 02:19:42 -05:00
MinPayloadLength ) ;
2022-05-04 08:58:50 -04:00
Request . SetStatus ( FPushRequest : : EStatus : : BelowMinSize ) ;
2021-12-08 02:19:42 -05:00
continue ;
}
2022-05-04 08:58:50 -04:00
if ( ! ShouldVirtualize ( Request . GetContext ( ) ) )
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-05-04 08:58:50 -04:00
* LexToString ( Request . GetIdentifier ( ) ) ,
* Request . GetContext ( ) ) ;
2022-01-28 15:29:01 -05:00
2022-05-04 08:58:50 -04:00
Request . SetStatus ( FPushRequest : : EStatus : : ExcludedByPackagPath ) ;
2022-01-28 15:29:01 -05:00
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 ;
}
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 ;
}
2022-05-09 03:53:57 -04:00
FConditionalScopeLock _ ( & DebugValues . ForceSingleThreadedCS , DebugValues . bSingleThreaded ) ;
2022-04-25 08:14:36 -04:00
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
2022-04-26 13:20:05 -04:00
// Debug operation to validate that the payload we just pushed can be retrieved from storage
if ( DebugValues . bValidateAfterPush & & 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 )
{
2022-05-04 08:58:50 -04:00
FCompressedBuffer ValidationPayload = PullDataFromBackend ( * Backend , Request . GetIdentifier ( ) ) ;
checkf ( Request . GetIdentifier ( ) = = ValidationPayload . GetRawHash ( ) ,
2021-12-08 02:19:42 -05:00
TEXT ( " [%s] Failed to pull payload '%s' after it was pushed to backend " ) ,
* Backend - > GetDebugName ( ) ,
2022-05-04 08:58:50 -04:00
* LexToString ( Request . GetIdentifier ( ) ) ) ;
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 )
{
2022-05-04 08:58:50 -04:00
Requests [ Index ] . SetStatus ( ValidatedRequests [ MappingIndex ] . GetStatus ( ) ) ;
2021-12-08 02:19:42 -05:00
}
}
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 ( ) ;
}
2022-05-09 03:53:57 -04:00
FConditionalScopeLock _ ( & DebugValues . ForceSingleThreadedCS , DebugValues . bSingleThreaded ) ;
2021-04-30 08:14:54 -04:00
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 ( ) ) ;
{
2022-05-09 03:53:57 -04:00
FConditionalScopeLock _ ( & DebugValues . ForceSingleThreadedCS , DebugValues . bSingleThreaded ) ;
2021-12-01 11:13:31 -05:00
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! " ) ) ;
2022-05-17 07:54:28 -04:00
}
TArray < FString > DisabledAssetTypesFromIni ;
if ( ConfigFile . GetArray ( TEXT ( " Core.ContentVirtualization " ) , TEXT ( " DisabledAsset " ) , DisabledAssetTypesFromIni ) > 0 )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " \t Virtualization is disabled for payloads of the following assets: " ) ) ;
DisabledAssetTypes . Reserve ( DisabledAssetTypesFromIni . Num ( ) ) ;
for ( const FString & AssetType : DisabledAssetTypesFromIni )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " \t \t %s " ) , * AssetType ) ;
DisabledAssetTypes . Add ( FName ( AssetType ) ) ;
}
}
2021-04-30 08:14:54 -04:00
}
2021-11-18 14:37:34 -05:00
void FVirtualizationManager : : ApplyDebugSettingsFromFromCmdline ( )
{
2022-04-25 08:14:36 -04:00
if ( FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " VA-SingleThreaded " ) ) )
{
DebugValues . bSingleThreaded = true ;
UE_LOG ( LogVirtualization , Warning , TEXT ( " Cmdline has set the virtualization system to run single threaded " ) ) ;
}
2022-04-26 13:20:05 -04:00
if ( FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " VA-ValidatePushes " ) ) )
{
DebugValues . bValidateAfterPush = true ;
UE_LOG ( LogVirtualization , Warning , TEXT ( " Cmdline has set the virtualization system to pull each payload after pushing to either local or persistent storage " ) ) ;
}
2022-04-27 04:10:01 -04:00
FString CmdlineGraphName ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -VA-BackendGraph= " ) , CmdlineGraphName ) )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " Backend graph overriden from the cmdline: '%s' " ) , * CmdlineGraphName ) ;
BackendGraphName = CmdlineGraphName ;
}
2021-11-18 14:37:34 -05:00
FString MissOptions ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -VA-MissBackends= " ) , MissOptions ) )
{
2022-04-25 02:24:53 -04:00
MissOptions . ParseIntoArray ( DebugValues . MissBackends , 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-25 02:24:53 -04:00
for ( const FString & Backend : DebugValues . MissBackends )
2021-11-18 14:37:34 -05:00
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t %s " ) , * Backend ) ;
}
}
2022-04-22 07:26:04 -04:00
2022-04-25 02:24:53 -04:00
DebugValues . MissChance = 0.0f ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -VA-MissChance= " ) , DebugValues . MissChance ) )
2022-04-22 07:26:04 -04:00
{
2022-04-25 02:24:53 -04:00
DebugValues . MissChance = FMath : : Clamp ( DebugValues . MissChance , 0.0f , 100.0f ) ;
2022-04-22 07:26:04 -04:00
2022-04-25 02:24:53 -04:00
UE_LOG ( LogVirtualization , Warning , TEXT ( " Cmdline has set a %.1f%% chance of a payload pull failing " ) , DebugValues . MissChance ) ;
2022-04-22 07:26:04 -04:00
}
2021-11-18 14:37:34 -05:00
}
2022-04-25 02:24:53 -04:00
void FVirtualizationManager : : RegisterConsoleCommands ( )
{
2022-04-25 08:14:36 -04:00
DebugValues . ConsoleObjects . Add ( IConsoleManager : : Get ( ) . RegisterConsoleCommand (
2022-04-25 02:24:53 -04:00
TEXT ( " VA.MissBackends " ) ,
TEXT ( " A debug commnad which can be used to disable payload pulling on one or more backends " ) ,
2022-04-25 08:14:36 -04:00
FConsoleCommandWithArgsAndOutputDeviceDelegate : : CreateRaw ( this , & FVirtualizationManager : : OnUpdateDebugMissBackendsFromConsole )
) ) ;
2022-04-25 02:24:53 -04:00
2022-04-25 08:14:36 -04:00
DebugValues . ConsoleObjects . Add ( IConsoleManager : : Get ( ) . RegisterConsoleCommand (
2022-04-25 02:24:53 -04:00
TEXT ( " VA.MissChance " ) ,
TEXT ( " A debug command which can be used to set the chance that a payload pull will fail " ) ,
2022-04-25 08:14:36 -04:00
FConsoleCommandWithArgsAndOutputDeviceDelegate : : CreateRaw ( this , & FVirtualizationManager : : OnUpdateDebugMissChanceFromConsole )
) ) ;
2022-04-25 02:24:53 -04:00
2022-04-25 08:14:36 -04:00
DebugValues . ConsoleObjects . Add ( IConsoleManager : : Get ( ) . RegisterConsoleCommand (
2022-04-25 02:24:53 -04:00
TEXT ( " VA.MissCount " ) ,
TEXT ( " A debug command which can be used to cause the next X number of payload pulls to fail " ) ,
2022-04-25 08:14:36 -04:00
FConsoleCommandWithArgsAndOutputDeviceDelegate : : CreateRaw ( this , & FVirtualizationManager : : OnUpdateDebugMissCountFromConsole )
) ) ;
DebugValues . ConsoleObjects . Add ( IConsoleManager : : Get ( ) . RegisterConsoleVariableRef (
TEXT ( " VA.SingleThreaded " ) ,
DebugValues . bSingleThreaded ,
TEXT ( " When set the asset virtualization system will only access backends in a single threaded manner " )
) ) ;
2022-04-26 13:20:05 -04:00
DebugValues . ConsoleObjects . Add ( IConsoleManager : : Get ( ) . RegisterConsoleVariableRef (
TEXT ( " VA.ValidatePushes " ) ,
DebugValues . bValidateAfterPush ,
TEXT ( " When set the asset virtualization system will pull each payload after pushing to either local or persistent storage " )
) ) ;
2022-04-25 02:24:53 -04:00
}
void FVirtualizationManager : : OnUpdateDebugMissBackendsFromConsole ( const TArray < FString > & Args , FOutputDevice & OutputDevice )
2022-04-21 03:15:13 -04:00
{
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 " ) )
{
2022-04-25 02:24:53 -04:00
DebugValues . MissBackends . Empty ( ) ;
2022-04-21 03:15:13 -04:00
UpdateBackendDebugState ( ) ;
}
else if ( Args [ 0 ] = = TEXT ( " list " ) )
{
2022-04-25 02:24:53 -04:00
if ( ! DebugValues . MissBackends . IsEmpty ( ) )
2022-04-21 03:15:13 -04:00
{
OutputDevice . Log ( TEXT ( " Disabled backends: " ) ) ;
2022-04-25 02:24:53 -04:00
for ( const FString & Backend : DebugValues . MissBackends )
2022-04-21 03:15:13 -04:00
{
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 " ) )
{
2022-04-25 02:24:53 -04:00
DebugValues . MissBackends . Empty ( Args . Num ( ) - 1 ) ;
2022-04-21 03:15:13 -04:00
for ( int32 Index = 1 ; Index < Args . Num ( ) ; + + Index )
{
2022-04-25 02:24:53 -04:00
DebugValues . MissBackends . Add ( Args [ Index ] ) ;
2022-04-21 03:15:13 -04:00
}
UpdateBackendDebugState ( ) ;
}
else
{
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid args for the VA.MissBackends command! " ) ) ;
}
}
2022-04-25 02:24:53 -04:00
void FVirtualizationManager : : OnUpdateDebugMissChanceFromConsole ( const TArray < FString > & Args , FOutputDevice & OutputDevice )
2022-04-22 07:26:04 -04:00
{
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 " ) )
{
2022-04-25 02:24:53 -04:00
OutputDevice . Logf ( TEXT ( " Current debug miss chance: %.1f%% " ) , DebugValues . MissChance ) ;
2022-04-22 07:26:04 -04:00
}
else if ( Args . Num ( ) = = 2 & & Args [ 0 ] = = TEXT ( " set " ) )
{
2022-04-25 02:24:53 -04:00
if ( : : LexTryParseString ( DebugValues . MissChance , * Args [ 1 ] ) )
2022-04-22 07:26:04 -04:00
{
2022-04-25 02:24:53 -04:00
DebugValues . MissChance = FMath : : Clamp ( DebugValues . MissChance , 0.0f , 100.0f ) ;
OutputDevice . Logf ( TEXT ( " Current debug miss chance set to %.1f%% " ) , DebugValues . MissChance ) ;
2022-04-22 07:26:04 -04:00
}
else
{
2022-04-25 02:24:53 -04:00
DebugValues . MissChance = 0.0f ;
2022-04-22 07:26:04 -04:00
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-25 02:24:53 -04:00
void FVirtualizationManager : : OnUpdateDebugMissCountFromConsole ( const TArray < FString > & Args , FOutputDevice & OutputDevice )
{
if ( Args . IsEmpty ( ) )
{
OutputDevice . Log ( TEXT ( " VA.MissCount command help " ) ) ;
OutputDevice . Log ( TEXT ( " This command allows you to set the next X number of payload pulls to fail " ) ) ;
OutputDevice . Log ( TEXT ( " " ) ) ;
OutputDevice . Log ( TEXT ( " Commands: " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissCount show - prints the current number of future payload pulls that will fail " ) ) ;
OutputDevice . Log ( TEXT ( " VA.MissChance set Num - Sets the number of future payload pulls to fail " ) ) ;
}
else if ( Args . Num ( ) = = 1 & & Args [ 0 ] = = TEXT ( " show " ) )
{
// DebugMissCount could end up negative if many threads are pulling at once, so clamp to 0 as the min value
const int32 Value = FMath : : Max ( DebugValues . MissCount . load ( std : : memory_order_relaxed ) , 0 ) ;
OutputDevice . Logf ( TEXT ( " The next '%d' payload pulls will fail " ) , Value ) ;
}
else if ( Args . Num ( ) = = 2 & & Args [ 0 ] = = TEXT ( " set " ) )
{
int32 ValueToSet = 0 ;
if ( : : LexTryParseString ( ValueToSet , * Args [ 1 ] ) )
{
DebugValues . MissCount . store ( ValueToSet , std : : memory_order_relaxed ) ;
OutputDevice . Logf ( TEXT ( " The next '%d' payload pulls have been set to fail " ) , ValueToSet ) ;
}
else
{
DebugValues . MissCount . store ( 0 , std : : memory_order_relaxed ) ;
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid value, the number of future payload pulls to fail has been set to zero " ) ) ;
}
}
else
{
OutputDevice . Log ( ELogVerbosity : : Error , TEXT ( " Invalid args for the VA.MissCount 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
{
2022-04-25 02:24:53 -04:00
if ( DebugValues . MissBackends . IsEmpty ( ) )
2022-04-21 03:15:13 -04:00
{
return false ;
}
2022-04-25 02:24:53 -04:00
if ( DebugValues . MissBackends [ 0 ] = = TEXT ( " All " ) )
2022-04-21 03:15:13 -04:00
{
return true ;
}
2022-04-25 02:24:53 -04:00
for ( const FString & Name : DebugValues . MissBackends )
2022-04-21 03:15:13 -04:00
{
if ( Name = = BackendConfigName )
{
return true ;
}
}
return false ;
}
2022-04-25 02:24:53 -04:00
bool FVirtualizationManager : : ShouldDebugFailPulling ( )
2022-04-22 07:26:04 -04:00
{
2022-04-25 02:24:53 -04:00
// We don't want to decrement on every function call to avoid DebugMissCount
// underflowing, so we only try to decrement if the count is positive.
// It doesn't really matter if the value ends up a little bit negative.
if ( DebugValues . MissCount . load ( std : : memory_order_relaxed ) > 0 )
{
if ( DebugValues . MissCount . fetch_sub ( 1 , std : : memory_order_relaxed ) > 0 )
{
return true ;
}
}
if ( DebugValues . MissChance = = 0.0f )
2022-04-22 07:26:04 -04:00
{
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 ;
2022-04-25 02:24:53 -04:00
return RandValue < = DebugValues . MissChance ;
2022-04-22 07:26:04 -04:00
}
}
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
2022-04-26 13:20:05 -04:00
// Debug operation to validate that the payload we just cached can be retrieved from storage
if ( DebugValues . bValidateAfterPush & & 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
2022-05-04 08:58:50 -04:00
if ( Request . GetStatus ( ) = = FPushRequest : : EStatus : : Success )
2021-12-08 02:19:42 -05:00
{
2022-01-25 04:29:34 -05:00
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ;
2022-05-04 08:58:50 -04:00
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes , Request . GetPayloadSize ( ) , 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 ( ) )
{
2022-04-25 02:24:53 -04:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Debug miss chance (%.1f%%) invoked when pulling payload '%s' " ) , DebugValues . MissChance , * LexToString ( Id ) ) ;
2022-04-22 07:26:04 -04:00
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 ) ;
}
2022-05-06 02:26:59 -04:00
UE_LOG ( LogVirtualization , VeryVerbose , TEXT ( " [%s] pulled payload '%s' " ) , * Backend - > GetDebugName ( ) , * LexToString ( Id ) ) ;
2022-04-22 07:26:04 -04:00
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