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
2023-06-20 09:12:28 -04:00
# include "AnalyticsEventAttribute.h"
2022-06-17 04:02:32 -04:00
# include "HAL/IConsoleManager.h"
2021-06-03 04:35:17 -04:00
# include "HAL/PlatformTime.h"
2022-04-22 08:55:44 -04:00
# include "Logging/MessageLog.h"
2023-06-26 06:21:23 -04:00
# include "Misc/App.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"
2023-06-20 09:12:28 -04:00
# include "Misc/MessageDialog.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-06-17 04:02:32 -04:00
# include "Misc/ScopedSlowTask.h"
2023-06-20 09:12:28 -04:00
# include "PackageRehydrationProcess.h"
2022-07-18 08:40:11 -04:00
# include "PackageVirtualizationProcess.h"
2021-06-03 04:35:17 -04:00
# include "ProfilingDebugging/CookStats.h"
2021-11-07 23:43:01 -05:00
# include "VirtualizationFilterSettings.h"
2023-09-25 06:48:33 -04:00
# include "VirtualizationUtilities.h"
2023-03-17 11:15:24 -04:00
2022-04-22 08:55:44 -04:00
# define LOCTEXT_NAMESPACE "Virtualization"
2022-03-08 11:22:45 -05:00
2023-02-15 10:32:09 -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
2023-02-15 10:32:09 -05:00
ENUM_CLASS_FLAGS ( FVirtualizationManager : : ECachingPolicy ) ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
// Can be defined as 1 by programs target.cs files force the backend connections
// to lazy initialize on first use rather than when the system is initialized.
# ifndef UE_VIRTUALIZATION_CONNECTION_LAZY_INIT
# define UE_VIRTUALIZATION_CONNECTION_LAZY_INIT 0
# endif //UE_VIRTUALIZATION_CONNECTION_LAZY_INIT
2022-10-21 22:27:40 -04:00
# define UE_INLINE_ALLOCATION_COUNT 4
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
// TODO: Move to RegisterConsoleCommands
static TAutoConsoleVariable < bool > CVarLazyInitConnections (
TEXT ( " VA.LazyInitConnections " ) ,
false ,
TEXT ( " When true the VA backends will defer creating their connections until first use " ) ) ;
2022-10-03 20:36:09 -04:00
//
# define UE_USE_GLOBAL_CVAR 1
# if UE_USE_GLOBAL_CVAR
static FAutoConsoleVariable CVarAllowPkgVirtualization (
TEXT ( " VA.AllowPkgVirtualization " ) ,
true ,
TEXT ( " When true submitting packages in the editor will no longer trigger the virtualization process " )
) ;
# endif // UE_USE_GLOBAL_CVAR
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 ;
} ;
2023-11-15 10:51:51 -05:00
/**
* Utility to disable ' GIsRunningUnattendedScript ' for a given scope .
* We only change ' GIsRunningUnattendedScript ' if called on the GameThread as the
* value is not thread safe and is generally only changed on that thread .
*
* We need this so that we can try to force a modal dialog to be shown during
* the editor if a payload fails to pull as our only other option is to
* terminate the process .
*/
struct FDisableUnattendedScriptGlobal
{
FDisableUnattendedScriptGlobal ( )
{
if ( IsInGameThread ( ) )
{
bOriginalValue = GIsRunningUnattendedScript ;
GIsRunningUnattendedScript = false ;
}
}
~ FDisableUnattendedScriptGlobal ( )
{
if ( IsInGameThread ( ) )
{
GIsRunningUnattendedScript = bOriginalValue ;
}
}
private :
bool bOriginalValue = false ;
} ;
2022-10-21 22:27:40 -04:00
/**
* Utility class to help manage pull requests . When created it will remove invalid and duplicate requests so
* that backends do not need to worry about them .
* It will also prune the list of requests after each backend pull so that we only make requests for payloads
* that have not yet been found and on request provide a list of payloads that should be cached .
* On destruction it will then write the results back to the original requests . Duplicate requests will share
* references to the same payload in memory . Once destroyed all of the original requests should have a success
* or error status and none should be listed as pending .
*
* NOTE : This is intended to be used by FVirtualizationManager : : PullDataFromAllBackends only , hence doing a
* few dangerous things like returning a TArrayView as we know it will not be misused . If this is ever taken
* into wider use we will have to revisit things like that .
*/
class FPullRequestCollection
{
public :
UE_NONCOPYABLE ( FPullRequestCollection ) ;
FPullRequestCollection ( ) = delete ;
FPullRequestCollection ( TArrayView < FPullRequest > InRequests )
: OriginalRequests ( InRequests )
{
CurrentRequests . Reserve ( OriginalRequests . Num ( ) ) ;
// Record each payload hash as we add it to CurrentRequests so that we only request duplicates once.
TSet < FIoHash , DefaultKeyFuncs < FIoHash > , TInlineSetAllocator < UE_INLINE_ALLOCATION_COUNT > > UniquePayloads ;
for ( FPullRequest & Request : OriginalRequests )
{
if ( Request . GetIdentifier ( ) . IsZero ( ) )
{
Request . SetError ( ) ;
UE_LOG ( LogVirtualization , Error , TEXT ( " Attempting to pull a virtualized payload with an invalid FIoHash " ) ) ;
}
else if ( ! UniquePayloads . Contains ( Request . GetIdentifier ( ) ) )
{
CurrentRequests . Add ( Request ) ;
UniquePayloads . Add ( Request . GetIdentifier ( ) ) ;
}
}
}
~ FPullRequestCollection ( )
{
for ( FPullRequest & Request : OriginalRequests )
{
if ( FCompressedBuffer * Payload = LoadedPayloads . Find ( Request . GetIdentifier ( ) ) )
{
Request . SetPayload ( * Payload ) ;
}
else
{
Request . SetError ( ) ;
}
}
}
/**
* Called after the requests from : : GetRequests have been pulled from a backend . Payloads that were
* successfully pulled will be removed from the request list and added to the LoadedPayloads map
* so that they can be assigned to the original requests later .
*
* @ param Backend The backend that the payloads were pulled from
* @ param bRequirePayloadsToCache Do we need a list of payloads that should be cached
*
* @ return A list of requests that now need to be cached ( if required )
*/
TArray < FPushRequest > OnPullCompleted ( const IVirtualizationBackend & Backend , bool bRequirePayloadsToCache )
{
TArray < FPushRequest > PayloadsToCache ;
if ( bRequirePayloadsToCache )
{
PayloadsToCache . Reserve ( CurrentRequests . Num ( ) ) ;
}
for ( int32 Index = 0 ; Index < CurrentRequests . Num ( ) ; )
{
const FPullRequest & Request = CurrentRequests [ Index ] ;
if ( Request . IsSuccess ( ) )
{
if ( bRequirePayloadsToCache )
{
PayloadsToCache . Emplace ( FPushRequest ( Request . GetIdentifier ( ) , Request . GetPayload ( ) , FString ( ) ) ) ;
}
LoadedPayloads . Add ( Request . GetIdentifier ( ) , Request . GetPayload ( ) ) ;
CurrentRequests . RemoveAtSwap ( Index ) ;
UE_LOG ( LogVirtualization , VeryVerbose , TEXT ( " [%s] pulled payload '%s' " ) , * Backend . GetDebugName ( ) , * LexToString ( Request . GetIdentifier ( ) ) ) ;
}
else
{
+ + Index ;
}
}
return PayloadsToCache ;
}
/** Return the current list of requests that need to be made */
TArrayView < FPullRequest > GetRequests ( )
{
return CurrentRequests ;
}
2023-10-17 03:58:43 -04:00
TArray < FIoHash > GetFailedPayloads ( ) const
{
TArray < FIoHash > FailedPayloads ;
for ( const FPullRequest & Request : CurrentRequests )
{
if ( ! Request . IsSuccess ( ) )
{
FailedPayloads . Add ( Request . GetIdentifier ( ) ) ;
}
}
return FailedPayloads ;
}
2023-03-17 11:15:24 -04:00
/** Returns if there are still requests that need servicing or not */
bool IsWorkComplete ( ) const
2022-10-21 22:27:40 -04:00
{
2023-03-17 11:15:24 -04:00
return CurrentRequests . IsEmpty ( ) ;
2022-10-21 22:27:40 -04:00
}
private :
TArrayView < FPullRequest > OriginalRequests ;
TMap < FIoHash , FCompressedBuffer , TInlineSetAllocator < UE_INLINE_ALLOCATION_COUNT > > LoadedPayloads ;
TArray < FPullRequest , TInlineAllocator < UE_INLINE_ALLOCATION_COUNT > > CurrentRequests ;
} ;
2023-03-16 11:23:43 -04:00
const TCHAR * LexToString ( EPackageFilterMode Value )
{
switch ( Value )
{
case EPackageFilterMode : : OptIn :
return TEXT ( " OptIn " ) ;
case EPackageFilterMode : : OptOut :
return TEXT ( " OptOut " ) ;
default :
checkNoEntry ( ) ;
return TEXT ( " " ) ;
}
}
2022-10-21 22:27:40 -04:00
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 ;
}
2022-10-26 17:26:09 -04:00
/**
* Utility to check if a cmdline switch is present in the commandline under either of two names .
* Useful when transitioning from one command line to another .
2022-10-20 02:20:37 -04:00
*/
2022-10-26 17:26:09 -04:00
static bool IsCmdLineParamSet ( const TCHAR * Cmd , const TCHAR * AlternativeCmd )
2022-10-20 02:20:37 -04:00
{
const TCHAR * CmdLine = FCommandLine : : Get ( ) ;
if ( FParse : : Param ( CmdLine , Cmd ) )
{
return true ;
}
2022-10-26 17:26:09 -04:00
if ( FParse : : Param ( CmdLine , AlternativeCmd ) )
{
return true ;
}
return false ;
}
/**
* Utility to check if a cmdline value is present in the commandline under either of two names .
* Useful when transitioning from one command line to another .
*/
template < typename T >
static bool IsCmdLineValueSet ( const TCHAR * Cmd , const TCHAR * AlternativeCmd , T & OutValue )
{
const TCHAR * CmdLine = FCommandLine : : Get ( ) ;
if ( FParse : : Value ( CmdLine , Cmd , OutValue ) )
{
return true ;
}
if ( FParse : : Value ( CmdLine , AlternativeCmd , OutValue ) )
2022-10-20 02:20:37 -04:00
{
return true ;
}
return false ;
}
2022-12-09 03:39:20 -05:00
/** Utility to set the same FPushResult on many requests at once */
static void SetPushRequestsResult ( TArrayView < FPushRequest > Requests , FPushResult Result )
{
for ( FPushRequest & Request : Requests )
{
Request . SetResult ( Result ) ;
}
}
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
2023-01-30 19:19:19 -05:00
/**
* FPackagePath has strict requirements on how file paths are formated in order for it to be
* able to accept them . In theory the paths that the virtualization system are given are
* already in a good shape but as we do not know where they come from we cannot trust them .
* This utility will iterate over a list of untrusted filepaths and call various FPaths methods
* on them to make sure that they are all correct .
*/
TArray < FString > SanitizeFilePaths ( TConstArrayView < FString > FilePaths )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( SanitizeFilePaths ) ;
TArray < FString > SanitizedPaths ;
SanitizedPaths . Reserve ( FilePaths . Num ( ) ) ;
for ( const FString & Path : FilePaths )
{
FString SanitizedPath = Path ;
FPaths : : NormalizeFilename ( SanitizedPath ) ;
FPaths : : RemoveDuplicateSlashes ( SanitizedPath ) ;
SanitizedPaths . Emplace ( MoveTemp ( SanitizedPath ) ) ;
}
return SanitizedPaths ;
}
2022-08-11 05:51:29 -04:00
/**
* Utility to make ' StorageType = = EStorageType : : Cache ' checks easier while EStorageType : : Local continues to exist .
* When the deprecated value is removed this can also be removed and code calling it can just check for EStorageType : : Cache */
bool IsCacheType ( EStorageType StorageType )
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
return StorageType = = EStorageType : : Local | | StorageType = = EStorageType : : Cache ;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
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
}
2022-08-19 19:36:58 -04:00
bool HasProfilingData ( const TMap < FString , FCookStats : : CallStats > & Stats )
{
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-07-28 07:27:57 -04:00
/** Returns true if we have gathered any profiling data at all */
bool HasProfilingData ( )
{
2022-08-19 19:36:58 -04:00
return HasProfilingData ( CacheStats ) | | HasProfilingData ( PushStats ) | | HasProfilingData ( PullStats ) ;
2021-07-28 07:27:57 -04:00
}
2021-06-03 04:35:17 -04:00
void LogStats ( )
{
2022-06-17 04:02:32 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " " ) ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " Virtualization ProfileData " ) ) ;
2024-05-13 08:55:45 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " ============================================================================================= " ) ) ;
2021-06-03 04:35:17 -04:00
2022-08-19 19:36:58 -04:00
if ( ! HasProfilingData ( ) )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " Skipping profile data as there was no activity to report " ) ) ;
return ; // Early out if we have no data
}
2022-10-11 09:52:11 -04:00
auto PrintStats = [ ] ( const TCHAR * Name , const TMap < FString , FCookStats : : CallStats > & Stats )
2021-09-01 07:03:59 -04:00
{
2022-10-11 09:52:11 -04:00
if ( HasProfilingData ( Stats ) )
{
2024-05-13 08:55:45 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " %-40s|%10s|%15s|%12s|%11s| " ) , Name , TEXT ( " TotalCount " ) , TEXT ( " TotalSize (MiB) " ) , TEXT ( " TotalTime(s) " ) , TEXT ( " AvgTime(ms) " ) ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " ----------------------------------------|----------|---------------|------------|-----------| " ) ) ;
2021-09-01 07:03:59 -04:00
2022-10-11 09:52:11 -04:00
for ( const auto & Iterator : Stats )
{
const int64 Count = Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
2024-05-13 08:55:45 -04:00
const double TotalTime = ( double ) Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles ) * FPlatformTime : : GetSecondsPerCycle ( ) ;
2023-05-08 09:40:17 -04:00
const double DataSizeMB = ( double ) Iterator . Value . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes ) / ( 1024.0f * 1024.0f ) ;
2024-05-13 08:55:45 -04:00
const double AvgTime = Count > 0 ? ( TotalTime * 1000.0 ) / static_cast < double > ( Count ) : 0.0 ;
2021-09-01 07:03:59 -04:00
2024-05-13 08:55:45 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " %-40.40s|%10lld|%15.1lf|%12.1lf|%11.0lf| " ) ,
2022-10-11 09:52:11 -04:00
* Iterator . Key ,
Count ,
DataSizeMB ,
2024-05-13 08:55:45 -04:00
TotalTime ,
AvgTime ) ;
2022-10-11 09:52:11 -04:00
}
2021-09-01 07:03:59 -04:00
2024-05-13 08:55:45 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " ============================================================================================= " ) ) ;
2022-10-11 09:52:11 -04:00
}
} ;
2021-06-03 04:35:17 -04:00
2022-10-11 09:52:11 -04:00
PrintStats ( TEXT ( " Caching Data " ) , CacheStats ) ;
PrintStats ( TEXT ( " Pushing Data " ) , PushStats ) ;
PrintStats ( TEXT ( " Pulling Data " ) , PullStats ) ;
2021-06-03 04:35:17 -04:00
}
# endif // ENABLE_COOK_STATS
} //namespace Profiling
2023-05-11 06:35:58 -04:00
FString FVirtualizationManager : : ConnectionHelpUrl ;
2020-10-21 17:56:05 -04:00
FVirtualizationManager : : FVirtualizationManager ( )
2022-09-22 18:02:50 -04:00
: bAllowPackageVirtualization ( true )
2023-02-15 10:32:09 -05:00
, CachingPolicy ( ECachingPolicy : : AlwaysCache )
2020-10-21 17:56:05 -04:00
, MinPayloadLength ( 0 )
2021-04-30 08:14:54 -04:00
, BackendGraphName ( TEXT ( " ContentVirtualizationBackendGraph_None " ) )
2022-07-22 03:58:27 -04:00
, VirtualizationProcessTag ( TEXT ( " #virtualized " ) )
2022-07-21 06:38:06 -04:00
, FilteringMode ( EPackageFilterMode : : OptOut )
2022-08-09 07:51:55 -04:00
, bFilterMapContent ( true )
2022-07-21 08:31:47 -04:00
, bAllowSubmitIfVirtualizationFailed ( false )
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
, bLazyInitConnections ( false )
2023-03-17 11:15:24 -04:00
, bUseLegacyErrorHandling ( true )
2023-04-17 07:45:19 -04:00
, bForceCachingOnPull ( false )
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
, bPendingBackendConnections ( false )
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
{
2022-10-03 20:36:09 -04:00
for ( const TPair < IConsoleVariable * , FDelegateHandle > & KV : DebugValues . ConsoleDelegateHandles )
{
IConsoleVariable * ConsoleVariable = KV . Key ;
const FDelegateHandle & Handle = KV . Value ;
ConsoleVariable - > OnChangedDelegate ( ) . Remove ( Handle ) ;
}
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
}
2023-07-27 03:27:10 -04:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Destroying backends " ) ) ;
2022-07-06 13:55:56 -04:00
2022-08-11 05:51:29 -04:00
CacheStorageBackends . Empty ( ) ;
2021-07-22 09:55:49 -04:00
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
2023-07-27 03:27:10 -04:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Virtualization manager destroyed " ) ) ;
2021-04-30 08:14:54 -04:00
}
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-08-11 05:51:29 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Initializing the virtualization manager... " ) ) ;
2022-06-14 12:11:57 -04:00
// TODO: Ideally we'd break this down further, or at least have a FScopedSlowTask for each
// backend initialization but the slow task system will only update the UI every 0.2 seconds
// so if we have too many small tasks we might show misleading data to the user, so it is
// better for us to have a single scope here at the top level and rely on UnrealInsights for
// detailed profiling unless we do something to how FScopedSlowTask updates the UI.
FScopedSlowTask SlowTask ( 1.0f , LOCTEXT ( " VAInitialize " , " Initializing virtualized asset system... " ) ) ;
SlowTask . EnterProgressFrame ( 1.0f ) ;
2022-03-08 11:22:45 -05:00
ProjectName = InitParams . ProjectName ;
ApplySettingsFromConfigFiles ( InitParams . ConfigFile ) ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
ApplySettingsFromFromCmdline ( ) ;
ApplySettingsFromCVar ( ) ;
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
2022-08-11 05:51:29 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Virtualization manager initialization completed " ) ) ;
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
{
2022-09-22 18:02:50 -04:00
if ( ! bAllowPackageVirtualization )
2022-01-28 03:48:55 -05:00
{
return false ;
}
switch ( StorageType )
{
2022-08-11 05:51:29 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-01-28 03:48:55 -05:00
case EStorageType : : Local :
2022-08-11 05:51:29 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
case EStorageType : : Cache :
2023-02-15 10:32:09 -05:00
return ! CacheStorageBackends . IsEmpty ( ) & & EnumHasAllFlags ( CachingPolicy , ECachingPolicy : : CacheOnPush ) ;
2022-01-28 03:48:55 -05:00
break ;
case EStorageType : : Persistent :
return ! PersistentStorageBackends . IsEmpty ( ) ;
break ;
default :
checkNoEntry ( ) ;
return false ;
break ;
}
}
2022-08-09 07:51:55 -04:00
EPayloadFilterReason FVirtualizationManager : : FilterPayload ( const UObject * Owner ) const
2022-05-17 07:54:28 -04:00
{
2022-08-09 07:51:55 -04:00
UE : : Virtualization : : EPayloadFilterReason PayloadFilter = UE : : Virtualization : : EPayloadFilterReason : : None ;
if ( ! ShouldVirtualizeAsset ( Owner ) )
2022-05-17 07:54:28 -04:00
{
2022-08-09 07:51:55 -04:00
PayloadFilter | = UE : : Virtualization : : EPayloadFilterReason : : Asset ;
2022-05-17 07:54:28 -04:00
}
2022-08-09 07:51:55 -04:00
// TODO: If we keep this feature long term then we might want to work this out in SavePackage.cpp and pass the info
// via FLinkerSave rather than the following code.
if ( bFilterMapContent )
2022-05-17 07:54:28 -04:00
{
2022-08-09 07:51:55 -04:00
if ( const UObject * Outer = Owner - > GetOutermostObject ( ) )
{
if ( const UClass * OuterClass = Outer - > GetClass ( ) )
{
const FName OuterClassName = OuterClass - > GetFName ( ) ;
if ( OuterClassName = = FName ( " Level " ) | |
OuterClassName = = FName ( " World " ) | |
OuterClassName = = FName ( " MapBuildDataRegistry " ) )
{
PayloadFilter | = UE : : Virtualization : : EPayloadFilterReason : : MapContent ;
}
}
}
2022-05-17 07:54:28 -04:00
}
2022-08-09 07:51:55 -04:00
return PayloadFilter ;
2022-05-17 07:54:28 -04:00
}
2022-07-21 08:31:47 -04:00
bool FVirtualizationManager : : AllowSubmitIfVirtualizationFailed ( ) const
{
return bAllowSubmitIfVirtualizationFailed ;
}
2021-12-08 02:19:42 -05:00
bool FVirtualizationManager : : PushData ( TArrayView < FPushRequest > Requests , EStorageType StorageType )
2021-04-30 08:14:54 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : PushData ) ;
2022-12-09 03:39:20 -05:00
// No requests always counts as success
2022-01-21 07:34:38 -05:00
if ( Requests . IsEmpty ( ) )
{
return true ;
}
2022-12-09 03:39:20 -05:00
// Attempting to virtualize when the system or process is disabled always counts as a failure
if ( ! IsEnabled ( ) | | ! bAllowPackageVirtualization )
{
SetPushRequestsResult ( Requests , FPushResult : : GetAsProcessDisabled ( ) ) ;
return false ;
}
FBackendArray & Backends = IsCacheType ( StorageType ) ? CacheStorageBackends : PersistentStorageBackends ;
if ( Backends . IsEmpty ( ) )
{
SetPushRequestsResult ( Requests , FPushResult : : GetAsNoBackend ( ) ) ;
return false ;
}
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-09-30 15:47:04 -04:00
Request . SetResult ( FPushResult : : GetAsInvalid ( ) ) ;
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-09-30 15:47:04 -04:00
Request . SetResult ( FPushResult : : GetAsFiltered ( EPayloadFilterReason : : MinSize ) ) ;
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-09-30 15:47:04 -04:00
Request . SetResult ( FPushResult : : GetAsFiltered ( EPayloadFilterReason : : Path ) ) ;
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 ;
}
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
EnsureBackendConnections ( ) ;
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 ;
2022-12-09 03:39:20 -05:00
2021-07-22 09:55:49 -04:00
for ( IVirtualizationBackend * Backend : Backends )
2020-10-21 17:56:05 -04:00
{
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
if ( Backend - > GetConnectionStatus ( ) ! = IVirtualizationBackend : : EConnectionStatus : : Connected )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Cannot push to backend '%s' as it is not connected " ) , * Backend - > GetDebugName ( ) ) ;
continue ;
}
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
{
2022-10-21 22:27:40 -04:00
for ( FPushRequest & PushRequest : ValidatedRequests )
2021-12-08 02:19:42 -05:00
{
2023-04-25 08:47:06 -04:00
FText Errors ;
2022-10-21 22:27:40 -04:00
FPullRequest PullRequest ( PushRequest . GetIdentifier ( ) ) ;
2023-04-25 08:47:06 -04:00
PullDataFromBackend ( * Backend , MakeArrayView ( & PullRequest , 1 ) , Errors ) ;
2022-10-21 22:27:40 -04:00
checkf ( PushRequest . GetIdentifier ( ) = = PullRequest . GetPayload ( ) . 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-10-21 22:27:40 -04:00
* LexToString ( PushRequest . 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-09-30 15:47:04 -04:00
Requests [ Index ] . SetResult ( ValidatedRequests [ MappingIndex ] . GetResult ( ) ) ;
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.
2022-08-11 05:51:29 -04:00
return IsCacheType ( StorageType ) ? ErrorCount < Backends . Num ( ) : ErrorCount = = 0 ;
2021-04-30 08:14:54 -04:00
}
2022-10-21 22:27:40 -04:00
bool FVirtualizationManager : : PullData ( TArrayView < FPullRequest > Requests )
2021-04-30 08:14:54 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : PullData ) ;
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-10-21 22:27:40 -04:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to pull payload(s) as there are no backends mounted!' " ) ) ;
return false ;
2021-04-30 08:14:54 -04:00
}
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
EnsureBackendConnections ( ) ;
2022-05-09 03:53:57 -04:00
FConditionalScopeLock _ ( & DebugValues . ForceSingleThreadedCS , DebugValues . bSingleThreaded ) ;
2021-04-30 08:14:54 -04:00
2022-10-21 22:27:40 -04:00
BroadcastEvent ( Requests , IVirtualizationSystem : : PullBegunNotification ) ;
2021-11-23 15:05:13 -05:00
2022-10-21 22:27:40 -04:00
PullDataFromAllBackends ( Requests ) ;
2021-11-23 15:05:13 -05:00
2022-10-21 22:27:40 -04:00
BroadcastEvent ( Requests , IVirtualizationSystem : : PullEndedNotification ) ;
bool bSuccess = true ;
for ( const FPullRequest & Request : Requests )
2022-04-22 07:26:04 -04:00
{
2022-10-21 22:27:40 -04:00
// Report failed pulls of valid identifiers
if ( ! Request . IsSuccess ( ) & & ! Request . GetIdentifier ( ) . IsZero ( ) )
{
GetNotificationEvent ( ) . Broadcast ( IVirtualizationSystem : : PullFailedNotification , Request . GetIdentifier ( ) ) ;
2021-11-23 15:05:13 -05:00
2022-10-21 22:27:40 -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 ( Request . GetIdentifier ( ) ) ) ;
2021-04-30 08:14:54 -04:00
2022-10-21 22:27:40 -04:00
bSuccess = false ;
}
if ( Request . IsSuccess ( ) )
{
2023-08-31 08:56:30 -04:00
checkf ( Request . GetIdentifier ( ) = = Request . GetPayload ( ) . GetRawHash ( ) , TEXT ( " Invalid payload for '%s' " ) , * LexToString ( Request . GetIdentifier ( ) ) ) ;
2022-10-21 22:27:40 -04:00
}
2024-01-26 11:09:38 -05:00
}
if ( bSuccess )
{
UnattendedFailureMsgCount = 0 ;
}
2022-10-21 22:27:40 -04:00
return bSuccess ;
2021-04-30 08:14:54 -04:00
}
2022-06-15 10:37:52 -04:00
EQueryResult FVirtualizationManager : : QueryPayloadStatuses ( TArrayView < const FIoHash > Ids , EStorageType StorageType , TArray < EPayloadStatus > & 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-06-15 10:37:52 -04:00
OutStatuses [ Index ] = Ids [ Index ] . IsZero ( ) ? EPayloadStatus : : Invalid : EPayloadStatus : : NotFound ;
2021-12-01 11:13:31 -05:00
}
2022-08-11 05:51:29 -04:00
FBackendArray & Backends = IsCacheType ( StorageType ) ? CacheStorageBackends : PersistentStorageBackends ;
2021-12-01 11:13:31 -05:00
TArray < int8 > HitCount ;
TArray < bool > Results ;
HitCount . SetNum ( Ids . Num ( ) ) ;
Results . SetNum ( Ids . Num ( ) ) ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
EnsureBackendConnections ( ) ;
2021-12-01 11:13:31 -05:00
{
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 )
{
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
if ( Backend - > GetConnectionStatus ( ) ! = IVirtualizationBackend : : EConnectionStatus : : Connected )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Cannot query backend '%s' as it is not connected " ) , * Backend - > GetDebugName ( ) ) ;
continue ;
}
2021-12-01 11:13:31 -05:00
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 )
{
2022-06-15 10:37:52 -04:00
OutStatuses [ Index ] = EPayloadStatus : : NotFound ;
2021-12-01 11:13:31 -05:00
}
else if ( HitCount [ Index ] = = Backends . Num ( ) )
{
2022-06-15 10:37:52 -04:00
OutStatuses [ Index ] = EPayloadStatus : : FoundAll ;
2021-12-01 11:13:31 -05:00
}
else
{
2022-06-15 10:37:52 -04:00
OutStatuses [ Index ] = EPayloadStatus : : 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
}
2023-01-10 09:15:11 -05:00
FVirtualizationResult FVirtualizationManager : : TryVirtualizePackages ( TConstArrayView < FString > PackagePaths , EVirtualizationOptions Options )
2022-03-23 11:14:47 -04:00
{
2023-01-10 09:15:11 -05:00
FVirtualizationResult Result ;
2022-09-22 18:06:58 -04:00
if ( IsEnabled ( ) & & IsPushingEnabled ( EStorageType : : Persistent ) )
2022-07-22 03:58:27 -04:00
{
2023-01-30 19:19:19 -05:00
const TArray < FString > SanitizedPaths = SanitizeFilePaths ( PackagePaths ) ;
UE : : Virtualization : : VirtualizePackages ( SanitizedPaths , Options , Result ) ;
2022-09-22 18:06:58 -04:00
2023-01-10 09:15:11 -05:00
if ( Result . WasSuccessful ( ) & & ! VirtualizationProcessTag . IsEmpty ( ) )
2022-09-22 18:06:58 -04:00
{
2023-01-10 09:15:11 -05:00
Result . DescriptionTags . Add ( FText : : FromString ( VirtualizationProcessTag ) ) ;
2022-09-22 18:06:58 -04:00
}
2022-07-22 03:58:27 -04:00
}
2022-03-23 11:14:47 -04:00
2023-01-10 09:15:11 -05:00
return Result ;
2022-03-23 11:14:47 -04:00
}
2023-01-10 09:15:11 -05:00
FRehydrationResult FVirtualizationManager : : TryRehydratePackages ( TConstArrayView < FString > PackagePaths , ERehydrationOptions Options )
Add a rehydration command to the stand alone virtualization tool, making it easier to reverse the effects of asset virtualization. Unlike previous processes, this one does not require that we load the package and will just manipulate the data storaged in the package trailer.
#rb Sebastian.Nordgren
#rnx
#jira UE-156436
#preflight 62c287f9a3568e30664eb94f
### VA Standalone Tool
- We now plan to add much more functionality to the tool than just virtualizing and submitting changelists, so to make this easier I am moving the tool towards a design where it should be fairly easy to add new functionality.
- Added FCommand, which is a base class for adding new functionality, simple derive from FCommand and hook it up at the appropriate locations.
-- In the future it should be possible for new command types to automatically register themselves to be initiated from the command line. There should be no need to edit UnrealVirtualizationToolApp to add a new command but this will be done as an additional work item.
-- At the moment FCommand comes with a number of utility methods to call that cover some common source control commands.
-- The original functionality has not yet been moved to the command system and so the code is a little bit weird at the moment. Updating older code to the new system will be done as an additional work item.
- FProject/FPlugin have been moved to their own code files.
### Rehydrate Command
- The rehydrate command will take a number of packages, check them out of source control and then attempt to virtualize them.
- At the moment the chekout logic is fairly basic, we just check out every package supplied, we don't check if the package is virtualized or not yet. This can be improved in additional work items. Ideally by the end of command the only packages that we have checked out should also be rehydrated.
- At the moment the command can either take a path of a specific package, a path of a directory to find packages in, or a changelist containing packages that should be rehydrated.
- A cleint spec (workspace) can optionally be provided, but if not supplied we will attempt to find a client spec for which to check out the packages.
- Currently we will check out the packages to the default change list.
### Rehydrate process
- Added the rehydration process in it's own code files in the virtualization module. Like the virtualization process this is exposed in a public header file and no via the Core interface which means it is very specific to our module/implementation.
- The process expects that the caller will have checked out any required packages from source control. It will treat being unable to update a package file as an error.
- Added PackageUtils.h/.cpp and moved some of the generic code from the virtualization process code there so that it can be shared by the rehydration process.
### Misc
Moving away from the using things like FPackagePath as that requires that the correct mount points have been registered for a project and at the moment (with the flakiness of FConfig*) it seems that the best idea would be to prefer absolute file paths where possible.
[CL 20982284 by paul chipchase in ue5-main branch]
2022-07-07 06:54:33 -04:00
{
2023-01-10 09:15:11 -05:00
FRehydrationResult Result ;
Add a rehydration command to the stand alone virtualization tool, making it easier to reverse the effects of asset virtualization. Unlike previous processes, this one does not require that we load the package and will just manipulate the data storaged in the package trailer.
#rb Sebastian.Nordgren
#rnx
#jira UE-156436
#preflight 62c287f9a3568e30664eb94f
### VA Standalone Tool
- We now plan to add much more functionality to the tool than just virtualizing and submitting changelists, so to make this easier I am moving the tool towards a design where it should be fairly easy to add new functionality.
- Added FCommand, which is a base class for adding new functionality, simple derive from FCommand and hook it up at the appropriate locations.
-- In the future it should be possible for new command types to automatically register themselves to be initiated from the command line. There should be no need to edit UnrealVirtualizationToolApp to add a new command but this will be done as an additional work item.
-- At the moment FCommand comes with a number of utility methods to call that cover some common source control commands.
-- The original functionality has not yet been moved to the command system and so the code is a little bit weird at the moment. Updating older code to the new system will be done as an additional work item.
- FProject/FPlugin have been moved to their own code files.
### Rehydrate Command
- The rehydrate command will take a number of packages, check them out of source control and then attempt to virtualize them.
- At the moment the chekout logic is fairly basic, we just check out every package supplied, we don't check if the package is virtualized or not yet. This can be improved in additional work items. Ideally by the end of command the only packages that we have checked out should also be rehydrated.
- At the moment the command can either take a path of a specific package, a path of a directory to find packages in, or a changelist containing packages that should be rehydrated.
- A cleint spec (workspace) can optionally be provided, but if not supplied we will attempt to find a client spec for which to check out the packages.
- Currently we will check out the packages to the default change list.
### Rehydrate process
- Added the rehydration process in it's own code files in the virtualization module. Like the virtualization process this is exposed in a public header file and no via the Core interface which means it is very specific to our module/implementation.
- The process expects that the caller will have checked out any required packages from source control. It will treat being unable to update a package file as an error.
- Added PackageUtils.h/.cpp and moved some of the generic code from the virtualization process code there so that it can be shared by the rehydration process.
### Misc
Moving away from the using things like FPackagePath as that requires that the correct mount points have been registered for a project and at the moment (with the flakiness of FConfig*) it seems that the best idea would be to prefer absolute file paths where possible.
[CL 20982284 by paul chipchase in ue5-main branch]
2022-07-07 06:54:33 -04:00
2023-01-30 19:19:19 -05:00
const TArray < FString > SanitizedPaths = SanitizeFilePaths ( PackagePaths ) ;
UE : : Virtualization : : RehydratePackages ( SanitizedPaths , Options , Result ) ;
2022-10-13 16:28:28 -04:00
2023-01-10 09:15:11 -05:00
return Result ;
2022-10-13 16:28:28 -04:00
}
ERehydrationResult FVirtualizationManager : : TryRehydratePackages ( TConstArrayView < FString > PackagePaths , uint64 PaddingAlignment , TArray < FText > & OutErrors , TArray < FSharedBuffer > & OutPackages , TArray < FRehydrationInfo > * OutInfo )
{
OutErrors . Reset ( ) ;
UE : : Virtualization : : RehydratePackages ( PackagePaths , PaddingAlignment , OutErrors , OutPackages , OutInfo ) ;
Add a rehydration command to the stand alone virtualization tool, making it easier to reverse the effects of asset virtualization. Unlike previous processes, this one does not require that we load the package and will just manipulate the data storaged in the package trailer.
#rb Sebastian.Nordgren
#rnx
#jira UE-156436
#preflight 62c287f9a3568e30664eb94f
### VA Standalone Tool
- We now plan to add much more functionality to the tool than just virtualizing and submitting changelists, so to make this easier I am moving the tool towards a design where it should be fairly easy to add new functionality.
- Added FCommand, which is a base class for adding new functionality, simple derive from FCommand and hook it up at the appropriate locations.
-- In the future it should be possible for new command types to automatically register themselves to be initiated from the command line. There should be no need to edit UnrealVirtualizationToolApp to add a new command but this will be done as an additional work item.
-- At the moment FCommand comes with a number of utility methods to call that cover some common source control commands.
-- The original functionality has not yet been moved to the command system and so the code is a little bit weird at the moment. Updating older code to the new system will be done as an additional work item.
- FProject/FPlugin have been moved to their own code files.
### Rehydrate Command
- The rehydrate command will take a number of packages, check them out of source control and then attempt to virtualize them.
- At the moment the chekout logic is fairly basic, we just check out every package supplied, we don't check if the package is virtualized or not yet. This can be improved in additional work items. Ideally by the end of command the only packages that we have checked out should also be rehydrated.
- At the moment the command can either take a path of a specific package, a path of a directory to find packages in, or a changelist containing packages that should be rehydrated.
- A cleint spec (workspace) can optionally be provided, but if not supplied we will attempt to find a client spec for which to check out the packages.
- Currently we will check out the packages to the default change list.
### Rehydrate process
- Added the rehydration process in it's own code files in the virtualization module. Like the virtualization process this is exposed in a public header file and no via the Core interface which means it is very specific to our module/implementation.
- The process expects that the caller will have checked out any required packages from source control. It will treat being unable to update a package file as an error.
- Added PackageUtils.h/.cpp and moved some of the generic code from the virtualization process code there so that it can be shared by the rehydration process.
### Misc
Moving away from the using things like FPackagePath as that requires that the correct mount points have been registered for a project and at the moment (with the flakiness of FConfig*) it seems that the best idea would be to prefer absolute file paths where possible.
[CL 20982284 by paul chipchase in ue5-main branch]
2022-07-07 06:54:33 -04:00
2022-07-21 08:31:47 -04:00
return OutErrors . IsEmpty ( ) ? ERehydrationResult : : Success : ERehydrationResult : : Failed ;
Add a rehydration command to the stand alone virtualization tool, making it easier to reverse the effects of asset virtualization. Unlike previous processes, this one does not require that we load the package and will just manipulate the data storaged in the package trailer.
#rb Sebastian.Nordgren
#rnx
#jira UE-156436
#preflight 62c287f9a3568e30664eb94f
### VA Standalone Tool
- We now plan to add much more functionality to the tool than just virtualizing and submitting changelists, so to make this easier I am moving the tool towards a design where it should be fairly easy to add new functionality.
- Added FCommand, which is a base class for adding new functionality, simple derive from FCommand and hook it up at the appropriate locations.
-- In the future it should be possible for new command types to automatically register themselves to be initiated from the command line. There should be no need to edit UnrealVirtualizationToolApp to add a new command but this will be done as an additional work item.
-- At the moment FCommand comes with a number of utility methods to call that cover some common source control commands.
-- The original functionality has not yet been moved to the command system and so the code is a little bit weird at the moment. Updating older code to the new system will be done as an additional work item.
- FProject/FPlugin have been moved to their own code files.
### Rehydrate Command
- The rehydrate command will take a number of packages, check them out of source control and then attempt to virtualize them.
- At the moment the chekout logic is fairly basic, we just check out every package supplied, we don't check if the package is virtualized or not yet. This can be improved in additional work items. Ideally by the end of command the only packages that we have checked out should also be rehydrated.
- At the moment the command can either take a path of a specific package, a path of a directory to find packages in, or a changelist containing packages that should be rehydrated.
- A cleint spec (workspace) can optionally be provided, but if not supplied we will attempt to find a client spec for which to check out the packages.
- Currently we will check out the packages to the default change list.
### Rehydrate process
- Added the rehydration process in it's own code files in the virtualization module. Like the virtualization process this is exposed in a public header file and no via the Core interface which means it is very specific to our module/implementation.
- The process expects that the caller will have checked out any required packages from source control. It will treat being unable to update a package file as an error.
- Added PackageUtils.h/.cpp and moved some of the generic code from the virtualization process code there so that it can be shared by the rehydration process.
### Misc
Moving away from the using things like FPackagePath as that requires that the correct mount points have been registered for a project and at the moment (with the flakiness of FConfig*) it seems that the best idea would be to prefer absolute file paths where possible.
[CL 20982284 by paul chipchase in ue5-main branch]
2022-07-07 06:54:33 -04:00
}
2022-06-17 04:02:32 -04:00
void FVirtualizationManager : : DumpStats ( ) const
{
# if ENABLE_COOK_STATS
Profiling : : LogStats ( ) ;
# endif // ENABLE_COOK_STATS
}
2024-05-14 07:59:24 -04:00
FPayloadActivityInfo FVirtualizationManager : : GetSystemStatistics ( ) 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 ;
}
2024-05-14 07:59:24 -04:00
TArray < FBackendStats > FVirtualizationManager : : GetBackendStatistics ( ) const
2021-11-18 14:37:34 -05:00
{
2024-05-14 07:59:24 -04:00
TArray < FBackendStats > Stats ;
2021-11-18 14:37:34 -05:00
# if ENABLE_COOK_STATS
2024-05-14 07:59:24 -04:00
Stats . Reserve ( AllBackends . Num ( ) ) ;
for ( const TUniquePtr < IVirtualizationBackend > & Backend : AllBackends )
2021-11-18 14:37:34 -05:00
{
2024-05-14 07:59:24 -04:00
FBackendStats & BackendStats = Stats . AddDefaulted_GetRef ( ) ;
BackendStats . DebugName = Backend - > GetDebugName ( ) ;
BackendStats . ConfigName = Backend - > GetConfigName ( ) ;
BackendStats . Type = PersistentStorageBackends . Contains ( Backend . Get ( ) ) ? EStorageType : : Persistent : EStorageType : : Cache ;
FPayloadActivityInfo & Info = BackendStats . PayloadActivity ;
2021-11-18 14:37:34 -05:00
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 ) ;
}
# endif // ENABLE_COOK_STATS
2024-05-14 07:59:24 -04:00
return Stats ;
2021-11-18 14:37:34 -05:00
}
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... " ) ) ;
2022-06-10 09:18:41 -04:00
2022-08-10 09:43:35 -04:00
const TCHAR * LegacyConfigSection = TEXT ( " Core.ContentVirtualization " ) ;
const TCHAR * ConfigSection = TEXT ( " Core.VirtualizationModule " ) ;
2023-03-16 11:23:43 -04:00
// Note that many options are doubled up as we are moving the options for this module from "Core.ContentVirtualization"
2022-06-10 09:18:41 -04:00
// to it's own specific "Core.VirtualizationModule" section. This duplication can be removed before we ship 5.1
2022-08-10 09:43:35 -04:00
2023-03-16 11:23:43 -04:00
// The backend graph is the most important value and so should come first for logging purposes
{
if ( ! ConfigFile . GetString ( LegacyConfigSection , TEXT ( " BackendGraph " ) , BackendGraphName ) )
{
ConfigFile . GetString ( ConfigSection , TEXT ( " BackendGraph " ) , BackendGraphName ) ;
}
UE_LOG ( LogVirtualization , Display , TEXT ( " \t BackendGraphName : %s " ) , * BackendGraphName ) ;
}
2022-08-10 09:43:35 -04:00
{
// This value was moved from Core.ContentVirtualization to Core.VirtualizationModule then renamed from
// 'EnablePushToBackend' to 'EnablePayloadVirtualization' so there are a few paths we need to cover here.
// This can also be cleaned up for 5.1 shipping.
2023-03-16 11:23:43 -04:00
if ( ConfigFile . GetBool ( LegacyConfigSection , TEXT ( " EnablePushToBackend " ) , bAllowPackageVirtualization ) )
2022-08-10 09:43:35 -04:00
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t Found legacy ini file setting [Core.ContentVirtualization].EnablePushToBackend, rename to [Core.VirtualizationModule].EnablePayloadVirtualization " ) ) ;
}
2023-03-16 11:23:43 -04:00
else if ( ConfigFile . GetBool ( ConfigSection , TEXT ( " EnablePushToBackend " ) , bAllowPackageVirtualization ) )
2022-08-10 09:43:35 -04:00
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t Found legacy ini file setting [Core.VirtualizationModule].EnablePushToBackend, rename to [Core.VirtualizationModule].EnablePayloadVirtualization " ) ) ;
}
else
{
2023-03-16 11:23:43 -04:00
ConfigFile . GetBool ( ConfigSection , TEXT ( " EnablePayloadVirtualization " ) , bAllowPackageVirtualization ) ;
2022-08-10 09:43:35 -04:00
}
2023-03-16 11:23:43 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t EnablePayloadVirtualization : %s " ) , bAllowPackageVirtualization ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-04-30 08:14:54 -04:00
}
2021-08-04 03:25:25 -04:00
{
2023-03-16 11:23:43 -04:00
bool bCacheOnPull = true ;
if ( ConfigFile . GetBool ( LegacyConfigSection , TEXT ( " EnableCacheAfterPull " ) , bCacheOnPull ) | |
ConfigFile . GetBool ( ConfigSection , TEXT ( " EnableCacheAfterPull " ) , bCacheOnPull ) )
2023-02-15 10:32:09 -05:00
{
2023-03-16 11:23:43 -04:00
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t EnableCacheAfterPull is deprecated, replace with 'EnableCacheOnPull=True|False' " ) ) ;
2023-02-15 10:32:09 -05:00
}
else
{
2023-03-16 11:23:43 -04:00
ConfigFile . GetBool ( ConfigSection , TEXT ( " EnableCacheOnPull " ) , bCacheOnPull ) ;
2023-02-15 10:32:09 -05:00
}
2023-03-16 11:23:43 -04:00
if ( ! bCacheOnPull )
2023-02-15 10:32:09 -05:00
{
2023-03-16 11:23:43 -04:00
EnumRemoveFlags ( CachingPolicy , ECachingPolicy : : CacheOnPull ) ;
2023-02-15 10:32:09 -05:00
}
2023-03-16 11:23:43 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t EnableCacheOnPull : %s " ) , EnumHasAllFlags ( CachingPolicy , ECachingPolicy : : CacheOnPull ) ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
}
{
bool bCacheOnPush = true ;
ConfigFile . GetBool ( ConfigSection , TEXT ( " EnableCacheOnPush " ) , bCacheOnPush ) ;
if ( ! bCacheOnPush )
2023-02-15 10:32:09 -05:00
{
2023-03-16 11:23:43 -04:00
EnumRemoveFlags ( CachingPolicy , ECachingPolicy : : CacheOnPush ) ;
2023-02-15 10:32:09 -05:00
}
2023-03-16 11:23:43 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t EnableCacheOnPush : %s " ) , EnumHasAllFlags ( CachingPolicy , ECachingPolicy : : CacheOnPush ) ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2021-08-04 03:25:25 -04:00
}
2021-04-30 08:14:54 -04:00
{
2023-03-16 11:23:43 -04:00
if ( ! ConfigFile . GetInt64 ( LegacyConfigSection , TEXT ( " MinPayloadLength " ) , MinPayloadLength ) )
{
ConfigFile . GetInt64 ( ConfigSection , TEXT ( " MinPayloadLength " ) , MinPayloadLength ) ;
}
UE_LOG ( LogVirtualization , Display , TEXT ( " \t MinPayloadLength : % " INT64_FMT ) , MinPayloadLength ) ;
2021-04-30 08:14:54 -04:00
}
{
2023-03-16 11:23:43 -04:00
ConfigFile . GetString ( ConfigSection , TEXT ( " VirtualizationProcessTag " ) , VirtualizationProcessTag ) ;
2022-07-22 03:58:27 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t VirtualizationProcessTag : %s " ) , * VirtualizationProcessTag ) ;
}
2023-03-16 11:23:43 -04:00
2022-07-22 03:58:27 -04:00
{
2023-03-16 11:23:43 -04:00
FString FilterModeString ;
if ( ConfigFile . GetString ( LegacyConfigSection , TEXT ( " FilterMode " ) , FilterModeString ) | |
ConfigFile . GetString ( ConfigSection , TEXT ( " FilterMode " ) , FilterModeString ) )
{
if ( ! LexTryParseString ( FilteringMode , FilterModeString ) )
{
UE_LOG ( LogVirtualization , Error , TEXT ( " [Core.VirtualizationModule].FilterMode was an invalid value! Allowed: 'OptIn'|'OptOut' Found '%s' " ) , * FilterModeString ) ;
}
}
UE_LOG ( LogVirtualization , Display , TEXT ( " \t FilterMode : %s " ) , LexToString ( FilteringMode ) ) ;
2022-07-22 03:58:27 -04:00
}
2022-02-16 01:27:09 -05:00
{
2023-03-16 11:23:43 -04:00
ConfigFile . GetBool ( ConfigSection , TEXT ( " FilterMapContent " ) , bFilterMapContent ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " \t FilterMapContent : %s " ) , bFilterMapContent ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
2022-02-16 01:27:09 -05:00
}
2023-03-16 11:23:43 -04:00
TArray < FString > DisabledAssetTypesFromIni ;
if ( ConfigFile . GetArray ( LegacyConfigSection , TEXT ( " DisabledAsset " ) , DisabledAssetTypesFromIni ) > 0 | |
ConfigFile . GetArray ( ConfigSection , TEXT ( " DisabledAsset " ) , DisabledAssetTypesFromIni ) > 0 )
2022-02-16 01:27:09 -05:00
{
2023-03-16 11:23:43 -04:00
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-07 23:43:01 -05:00
2023-03-16 11:23:43 -04:00
{
ConfigFile . GetBool ( ConfigSection , TEXT ( " AllowSubmitIfVirtualizationFailed " ) , bAllowSubmitIfVirtualizationFailed ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " \t AllowSubmitIfVirtualizationFailed : %s " ) , bAllowSubmitIfVirtualizationFailed ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
}
# if UE_VIRTUALIZATION_CONNECTION_LAZY_INIT == 0
ConfigFile . GetBool ( ConfigSection , TEXT ( " LazyInitConnections " ) , bLazyInitConnections ) ;
2023-06-26 06:21:23 -04:00
if ( bLazyInitConnections )
{
// Check if we should override the value of bLazyInitConnections depending on if the current process is interactive or not
bool bDisableLazyInitIfInteractive = false ;
ConfigFile . GetBool ( ConfigSection , TEXT ( " DisableLazyInitIfInteractive " ) , bDisableLazyInitIfInteractive ) ;
const bool bIsUsingSlate = UE_VA_WITH_SLATE ! = 0 ;
const bool bIsUnattended = FApp : : IsUnattended ( ) ;
const bool bIsRunningCommandlet = IsRunningCommandlet ( ) ;
const bool bIsInteractive = bIsUsingSlate & & ! bIsUnattended & & ! bIsRunningCommandlet ;
if ( bDisableLazyInitIfInteractive & & bIsInteractive )
{
bLazyInitConnections = false ;
}
}
2023-03-16 11:23:43 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t LazyInitConnections : %s " ) , bLazyInitConnections ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
# else
bLazyInitConnections = true ;
UE_LOG ( LogVirtualization , Display , TEXT ( " \t LazyInitConnections : true (set by code) " ) ) ;
# endif //UE_VIRTUALIZATION_CONNECTION_LAZY_INIT
2023-03-17 11:15:24 -04:00
{
ConfigFile . GetBool ( ConfigSection , TEXT ( " UseLegacyErrorHandling " ) , bUseLegacyErrorHandling ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " \t UseLegacyErrorHandling : %s " ) , bUseLegacyErrorHandling ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
}
2023-03-20 07:40:06 -04:00
{
ConfigFile . GetString ( ConfigSection , TEXT ( " PullErrorAdditionalMsg " ) , PullErrorAdditionalMsg ) ;
// This value is not echoed to the log file, as seeing an error string there might confuse users
}
2023-05-11 06:35:58 -04:00
{
ConfigFile . GetString ( ConfigSection , TEXT ( " ConnectionHelpUrl " ) , ConnectionHelpUrl ) ;
// This value is not echoed to the log file
}
2023-03-20 07:40:06 -04:00
2023-04-17 07:45:19 -04:00
{
ConfigFile . GetBool ( ConfigSection , TEXT ( " ForceCachingOnPull " ) , bForceCachingOnPull ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " \t ForceCachingOnPull : %s " ) , bForceCachingOnPull ? TEXT ( " true " ) : TEXT ( " false " ) ) ;
}
2024-01-26 11:09:38 -05:00
{
ConfigFile . GetInt ( ConfigSection , TEXT ( " UnattendedRetryTimer " ) , UnattendedRetryTimer ) ;
ConfigFile . GetInt ( ConfigSection , TEXT ( " UnattendedRetryCount " ) , UnattendedRetryCount ) ;
if ( ShouldRetryWhenUnattended ( ) )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " \t UnattendedRetryTimer : %d retries every %d(s) " ) , UnattendedRetryCount , UnattendedRetryTimer ) ;
}
else
{
UE_LOG ( LogVirtualization , Display , TEXT ( " \t UnattendedRetries : disabled " ) ) ;
}
}
2023-02-14 07:19:53 -05:00
// Deprecated
2021-11-07 23:43:01 -05:00
{
2023-02-14 07:19:53 -05:00
bool bDummyValue = true ;
if ( ConfigFile . GetBool ( LegacyConfigSection , TEXT ( " FilterEngineContent " ) , bDummyValue ) | |
ConfigFile . GetBool ( ConfigSection , TEXT ( " FilterEngineContent " ) , bDummyValue ) )
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t FilterEngineContent is now deprecated (engine content is never virtualized) " ) ) ;
}
if ( ConfigFile . GetBool ( LegacyConfigSection , TEXT ( " FilterEnginePluginContent " ) , bDummyValue ) | |
ConfigFile . GetBool ( ConfigSection , TEXT ( " FilterEnginePluginContent " ) , bDummyValue ) )
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t FilterEnginePluginContent is now deprecated (engine content is never virtualized) " ) ) ;
}
2022-05-17 07:54:28 -04:00
}
2022-06-10 09:18:41 -04:00
// Check for any legacy settings and print them out (easier to do this in one block rather than one and time)
{
2023-06-20 09:12:28 -04:00
// Entries that are allows to be in [Core.ContentVirtualization]
static const TArray < FString > AllowedEntries = { TEXT ( " SystemName " ) , TEXT ( " LazyInit " ) , TEXT ( " InitPreSlate " ) } ;
2022-08-19 19:15:42 -04:00
TArray < FString > LegacyEntries ;
2023-09-15 13:39:02 -04:00
if ( const FConfigSection * LegacySection = ConfigFile . FindSection ( LegacyConfigSection ) )
2022-06-10 09:18:41 -04:00
{
2022-08-19 19:15:42 -04:00
for ( const TPair < FName , FConfigValue > & It : * LegacySection )
2022-06-10 09:18:41 -04:00
{
2022-08-19 19:15:42 -04:00
FString Name = It . Key . ToString ( ) ;
if ( ! AllowedEntries . Contains ( Name ) )
2022-06-10 09:18:41 -04:00
{
2022-08-19 19:15:42 -04:00
LegacyEntries . Add ( MoveTemp ( Name ) ) ;
2022-06-10 09:18:41 -04:00
}
}
}
2022-08-19 19:15:42 -04:00
if ( ! LegacyEntries . IsEmpty ( ) )
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t Found %d legacy ini file settings under [Core.ContentVirtualization] that should be moved to [Core.VirtualizationModule] " ) , LegacyEntries . Num ( ) ) ;
for ( const FString & LegacyEntry : LegacyEntries )
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t \t %s " ) , * LegacyEntry ) ;
}
}
2022-06-10 09:18:41 -04:00
}
2021-04-30 08:14:54 -04:00
}
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
void FVirtualizationManager : : ApplySettingsFromFromCmdline ( )
{
2022-10-26 17:26:09 -04:00
if ( ! bLazyInitConnections & & IsCmdLineParamSet ( TEXT ( " VALazyInitConnections " ) , TEXT ( " VA-LazyInitConnections " ) ) )
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
{
bLazyInitConnections = true ;
UE_LOG ( LogVirtualization , Display , TEXT ( " Cmdline has set the virtualization system backends to lazy init their connections " ) ) ;
}
2022-08-23 19:20:59 -04:00
2022-10-26 17:26:09 -04:00
if ( bAllowPackageVirtualization & & IsCmdLineParamSet ( TEXT ( " VASkipPkgVirtualization " ) , TEXT ( " VA-SkipPkgVirtualization " ) ) )
2022-08-23 19:20:59 -04:00
{
2022-09-22 18:02:50 -04:00
bAllowPackageVirtualization = false ;
UE_LOG ( LogVirtualization , Warning , TEXT ( " The virtualization process has been disabled via the command line " ) ) ;
2022-08-23 19:20:59 -04:00
}
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
}
void FVirtualizationManager : : ApplySettingsFromCVar ( )
{
if ( ! bLazyInitConnections & & CVarLazyInitConnections . GetValueOnAnyThread ( ) )
{
bLazyInitConnections = true ;
UE_LOG ( LogVirtualization , Display , TEXT ( " CVar has set the virtualization system backends to lazy init their connections " ) ) ;
}
}
2021-11-18 14:37:34 -05:00
void FVirtualizationManager : : ApplyDebugSettingsFromFromCmdline ( )
{
2022-10-26 17:26:09 -04:00
if ( IsCmdLineParamSet ( TEXT ( " VASingleThreaded " ) , TEXT ( " VA-SingleThreaded " ) ) )
2022-04-25 08:14:36 -04:00
{
DebugValues . bSingleThreaded = true ;
UE_LOG ( LogVirtualization , Warning , TEXT ( " Cmdline has set the virtualization system to run single threaded " ) ) ;
}
2022-10-26 17:26:09 -04:00
if ( IsCmdLineParamSet ( TEXT ( " VAValidatePushes " ) , TEXT ( " VA-ValidatePushes " ) ) )
2022-04-26 13:20:05 -04:00
{
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 ;
2022-10-26 17:26:09 -04:00
if ( IsCmdLineValueSet ( TEXT ( " -VABackendGraph= " ) , TEXT ( " -VA-BackendGraph= " ) , CmdlineGraphName ) )
2022-04-27 04:10:01 -04:00
{
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 ;
2022-10-26 17:26:09 -04:00
if ( IsCmdLineValueSet ( TEXT ( " -VAMissBackends= " ) , TEXT ( " -VA-MissBackends= " ) , MissOptions ) )
2021-11-18 14:37:34 -05:00
{
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 ;
2022-10-26 17:26:09 -04:00
if ( IsCmdLineValueSet ( TEXT ( " -VAMissChance= " ) , 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-09-22 18:02:50 -04:00
{
2022-10-03 20:36:09 -04:00
# if UE_USE_GLOBAL_CVAR
IConsoleVariable * Handle = IConsoleManager : : Get ( ) . FindConsoleVariable ( TEXT ( " VA.AllowPkgVirtualization " ) ) ;
if ( Handle ! = nullptr )
{
auto Callback = [ this ] ( IConsoleVariable * CVar )
{
this - > bAllowPackageVirtualization = CVar - > GetBool ( ) ;
if ( this - > bAllowPackageVirtualization )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " The virtualization process has been enabled via the cvar 'VA.SkipPkgVirtualization' " ) ) ;
}
else
{
UE_LOG ( LogVirtualization , Display , TEXT ( " The virtualization process has been disabled via the cvar 'VA.SkipPkgVirtualization' " ) ) ;
}
} ;
FDelegateHandle CallbackHandle = Handle - > OnChangedDelegate ( ) . AddLambda ( MoveTemp ( Callback ) ) ;
if ( bAllowPackageVirtualization ! = Handle - > GetBool ( ) )
{
Callback ( Handle ) ;
}
DebugValues . ConsoleDelegateHandles . Add ( { Handle , CallbackHandle } ) ;
}
else
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " CVar VA.AllowPkgVirtualization could not be found and will not function " ) ) ;
}
# else
2022-09-22 18:02:50 -04:00
bool bOriginalValue = bAllowPackageVirtualization ;
IConsoleVariable * Handle = IConsoleManager : : Get ( ) . RegisterConsoleVariableRef (
TEXT ( " VA.AllowPkgVirtualization " ) ,
bAllowPackageVirtualization ,
TEXT ( " When true submitting packages in the editor will no longer trigger the virtualization process " )
) ;
auto Callback = [ ] ( IConsoleVariable * CVar )
{
if ( CVar - > GetBool ( ) )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " The virtualization process has been enabled via the cvar 'VA.SkipPkgVirtualization' " ) ) ;
}
else
{
UE_LOG ( LogVirtualization , Display , TEXT ( " The virtualization process has been disabled via the cvar 'VA.SkipPkgVirtualization' " ) ) ;
}
} ;
// Log the change if the cvar was modified on the commandline
if ( bOriginalValue ! = bAllowPackageVirtualization )
{
Callback ( Handle ) ;
}
Handle - > OnChangedDelegate ( ) . AddLambda ( MoveTemp ( Callback ) ) ;
DebugValues . ConsoleObjects . Add ( Handle ) ;
2022-10-03 20:36:09 -04:00
# endif // UE_USE_GLOBAL_CVAR
2022-09-22 18:02:50 -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.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-08-11 05:51:29 -04:00
ParseHierarchy ( ConfigFile , GraphName , TEXT ( " CacheStorageHierarchy " ) , TEXT ( " LocalStorageHierarchy " ) , FactoryLookupTable , CacheStorageBackends ) ;
ParseHierarchy ( ConfigFile , GraphName , TEXT ( " PersistentStorageHierarchy " ) , nullptr , 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-08-11 05:51:29 -04:00
void FVirtualizationManager : : ParseHierarchy ( const FConfigFile & ConfigFile , const TCHAR * GraphName , const TCHAR * HierarchyKey , const TCHAR * LegacyHierarchyKey , const FRegistedFactories & FactoryLookupTable , FBackendArray & PushArray )
2021-07-22 09:55:49 -04:00
{
2022-08-11 05:51:29 -04:00
bool bFoundHierarchy = false ;
2021-04-30 08:14:54 -04:00
FString HierarchyData ;
2022-08-11 05:51:29 -04:00
if ( LegacyHierarchyKey ! = nullptr & & ConfigFile . GetValue ( GraphName , LegacyHierarchyKey , HierarchyData ) )
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " \t Found legacy HierarchyKey '%s', rename to '%s' " ) , LegacyHierarchyKey , HierarchyKey ) ;
bFoundHierarchy = true ;
}
else if ( ConfigFile . GetValue ( GraphName , HierarchyKey , HierarchyData ) )
{
bFoundHierarchy = true ;
}
if ( bFoundHierarchy )
2021-04-30 08:14:54 -04:00
{
2022-06-03 10:04:41 -04:00
if ( HierarchyData . IsEmpty ( ) )
{
2022-08-11 05:51:29 -04:00
UE_LOG ( LogVirtualization , Fatal , TEXT ( " \t The '%s' entry for backend graph '%s' is empty [ini=%s]. " ) , HierarchyKey , GraphName , * GEngineIni ) ;
2022-06-03 10:04:41 -04:00
}
const TArray < FString > Entries = ParseEntries ( HierarchyData ) ;
2022-08-11 05:51:29 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t The '%s' has %d backend(s) " ) , HierarchyKey , Entries . Num ( ) ) ;
2022-06-03 10:04:41 -04:00
for ( const FString & Entry : Entries )
{
CreateBackend ( ConfigFile , GraphName , Entry , FactoryLookupTable , PushArray ) ;
}
2021-04-30 08:14:54 -04:00
}
2022-06-03 10:04:41 -04:00
else
2021-04-30 08:14:54 -04:00
{
2022-08-11 05:51:29 -04:00
UE_LOG ( LogVirtualization , Display , TEXT ( " \t No entries for '%s' in the content virtualization backend graph '%s' [ini=%s]. " ) , HierarchyKey , GraphName , * GEngineIni ) ;
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 ) )
{
2022-10-27 11:33:43 -04:00
// The read only flag can be applied to any backend so we check for it and apply it at this point
bool bReadOnly = false ;
FParse : : Bool ( * Cmdine , TEXT ( " ReadOnly= " ) , bReadOnly ) ;
if ( bReadOnly & & Backend - > DisableOperation ( IVirtualizationBackend : : EOperations : : Push ) )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " The backend '%s' was set to readonly by the config file! " ) , * Backend - > GetDebugName ( ) ) ;
}
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 ( ) ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
check ( BackendRef ! = nullptr ) ;
2021-07-22 09:55:49 -04:00
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
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
// We immediately try to connect once the backend has been added.
// In the future this will be made async to avoid blocking the GameThread on startup
if ( ! bLazyInitConnections )
{
BackendRef - > Connect ( ) ;
}
else
{
bPendingBackendConnections = true ;
}
2021-07-22 09:55:49 -04:00
COOK_STAT ( Profiling : : CreateStats ( * BackendRef ) ) ;
}
2023-04-25 08:47:06 -04:00
bool FVirtualizationManager : : IsPersistentBackend ( IVirtualizationBackend & Backend )
{
return PersistentStorageBackends . Contains ( & Backend ) ;
}
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
void FVirtualizationManager : : EnsureBackendConnections ( )
{
if ( bPendingBackendConnections )
{
// Only allow one thread to initialize the system at a time
2024-05-20 03:21:37 -04:00
static FMutex Mutex ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
2024-05-20 03:21:37 -04:00
UE : : TUniqueLock _ ( Mutex ) ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
if ( bPendingBackendConnections )
{
for ( const TUniquePtr < IVirtualizationBackend > & Backend : AllBackends )
{
Backend - > Connect ( ) ;
}
bPendingBackendConnections = false ;
}
}
}
2023-04-17 07:45:19 -04:00
void FVirtualizationManager : : CachePayloads ( TArrayView < FPushRequest > Requests , const IVirtualizationBackend * BackendSource , IVirtualizationBackend : : EPushFlags Flags )
2021-07-22 09:55:49 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FVirtualizationManager : : CachePayload ) ;
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
check ( ! bPendingBackendConnections ) ;
2021-07-22 09:55:49 -04:00
// We start caching at the first (assumed to be fastest) local cache backend.
2022-08-11 05:51:29 -04:00
for ( IVirtualizationBackend * BackendToCache : CacheStorageBackends )
2021-07-22 09:55:49 -04:00
{
2022-10-21 22:27:40 -04:00
// We stop once we reach the backend that the payloads were first pulled from
2021-07-22 09:55:49 -04:00
if ( BackendToCache = = BackendSource )
{
2022-10-21 22:27:40 -04:00
return ;
2021-07-22 09:55:49 -04:00
}
2024-05-06 02:37:38 -04:00
// Reset any previous caching results
for ( FPushRequest & Request : Requests )
{
Request . ResetResult ( ) ;
}
2023-04-17 07:45:19 -04:00
const bool bResult = TryCacheDataToBackend ( * BackendToCache , Requests , Flags ) ;
2022-10-21 22:27:40 -04:00
if ( ! bResult )
{
for ( const FPushRequest & Request : Requests )
{
2024-04-26 09:09:12 -04:00
UE_CLOG ( Request . GetResult ( ) . WasError ( ) , LogVirtualization , Warning , TEXT ( " Failed to cache payload '%s' to backend '%s' " ) ,
* LexToString ( Request . GetIdentifier ( ) ) ,
* BackendToCache - > GetDebugName ( ) ) ;
2022-10-21 22:27:40 -04:00
}
}
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
{
2022-10-21 22:27:40 -04:00
for ( const FPushRequest & Request : Requests )
{
2023-04-25 08:47:06 -04:00
FText Errors ;
2022-10-21 22:27:40 -04:00
FPullRequest ValidationRequest ( Request . GetIdentifier ( ) ) ;
2023-04-25 08:47:06 -04:00
PullDataFromBackend ( * BackendToCache , MakeArrayView ( & ValidationRequest , 1 ) , Errors ) ;
2022-10-21 22:27:40 -04:00
checkf ( Request . GetPayload ( ) . GetRawHash ( ) = = ValidationRequest . GetPayload ( ) . GetRawHash ( ) ,
2021-11-18 14:37:34 -05:00
TEXT ( " [%s] Failed to pull payload '%s' after it was cached to backend " ) ,
2022-10-21 22:27:40 -04:00
* BackendToCache - > GetDebugName ( ) ,
* LexToString ( Request . GetIdentifier ( ) ) ) ;
}
2021-09-01 07:03:59 -04:00
}
2021-07-22 09:55:49 -04:00
}
2020-10-21 17:56:05 -04:00
}
2023-04-17 07:45:19 -04:00
bool FVirtualizationManager : : TryCacheDataToBackend ( IVirtualizationBackend & Backend , TArrayView < FPushRequest > Requests , IVirtualizationBackend : : EPushFlags Flags )
2021-09-01 07:03:59 -04:00
{
2022-10-21 22:27:40 -04:00
COOK_STAT ( FCookStats : : CallStats & Stats = Profiling : : GetCacheStats ( Backend ) ) ;
COOK_STAT ( FCookStats : : FScopedStatsCounter Timer ( Stats ) ) ;
COOK_STAT ( Timer . TrackCyclesOnly ( ) ) ;
2022-09-08 19:35:36 -04:00
2023-04-17 07:45:19 -04:00
if ( Backend . PushData ( Requests , Flags ) )
2021-09-01 07:03:59 -04:00
{
2022-10-21 22:27:40 -04:00
# if ENABLE_COOK_STATS
const bool bIsInGameThread = IsInGameThread ( ) ;
2023-04-17 08:06:12 -04:00
bool bWasDataPushed = false ;
2022-10-21 22:27:40 -04:00
for ( const FPushRequest & Request : Requests )
2022-09-30 15:47:04 -04:00
{
2022-10-21 22:27:40 -04:00
if ( Request . GetResult ( ) . WasPushed ( ) )
{
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ;
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes , Request . GetPayload ( ) . GetCompressedSize ( ) , bIsInGameThread ) ;
2023-04-17 08:06:12 -04:00
bWasDataPushed = true ;
2024-05-06 02:37:38 -04:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " [%s] Cached payload '%s' " ) , * Backend . GetDebugName ( ) , * LexToString ( Request . GetIdentifier ( ) ) ) ;
2022-10-21 22:27:40 -04:00
}
2022-09-30 15:47:04 -04:00
}
2023-04-17 08:06:12 -04:00
if ( bWasDataPushed )
{
Timer . AddHit ( 0 ) ;
}
2022-10-21 22:27:40 -04:00
# endif // ENABLE_COOK_STATS
2022-09-30 15:47:04 -04:00
2022-09-08 19:35:36 -04:00
return true ;
}
else
{
return false ;
2021-09-01 07:03:59 -04:00
}
}
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
2023-04-14 10:13:46 -04:00
const bool bPushResult = Backend . PushData ( Requests , IVirtualizationBackend : : EPushFlags : : None ) ;
2022-01-25 04:29:34 -05:00
# 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
{
2022-09-30 15:47:04 -04:00
if ( Request . GetResult ( ) . WasPushed ( ) )
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-10-21 22:27:40 -04:00
void FVirtualizationManager : : PullDataFromAllBackends ( TArrayView < FPullRequest > Requests )
2022-04-22 07:26:04 -04:00
{
if ( ShouldDebugFailPulling ( ) )
{
2022-10-21 22:27:40 -04:00
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Debug miss chance (%.1f%%) invoked " ) , DebugValues . MissChance ) ;
return ;
2022-04-22 07:26:04 -04:00
}
2022-10-21 22:27:40 -04:00
FPullRequestCollection RequestsCollection ( Requests ) ;
2023-03-17 11:15:24 -04:00
while ( true )
2022-04-22 07:26:04 -04:00
{
2023-04-25 08:47:06 -04:00
TStringBuilder < 512 > BackendErrors ;
2023-03-17 11:15:24 -04:00
for ( IVirtualizationBackend * Backend : PullEnabledBackends )
2022-04-22 07:26:04 -04:00
{
2023-03-17 11:15:24 -04:00
check ( Backend ! = nullptr ) ;
if ( Backend - > IsOperationDebugDisabled ( IVirtualizationBackend : : EOperations : : Pull ) )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Pulling from backend '%s' is debug disabled " ) , * Backend - > GetDebugName ( ) ) ;
continue ;
}
if ( Backend - > GetConnectionStatus ( ) ! = IVirtualizationBackend : : EConnectionStatus : : Connected )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Cannot pull from backend '%s' as it is not connected " ) , * Backend - > GetDebugName ( ) ) ;
continue ;
}
2023-04-25 08:47:06 -04:00
FText Errors ;
PullDataFromBackend ( * Backend , RequestsCollection . GetRequests ( ) , Errors ) ;
// We only want to report errors from persistent backends, cached backends are allowed to fail.
if ( ! Errors . IsEmpty ( ) & & IsPersistentBackend ( * Backend ) )
{
if ( BackendErrors . Len ( ) ! = 0 )
{
BackendErrors < < LINE_TERMINATOR ;
}
BackendErrors < < Backend - > GetDebugName ( ) < < TEXT ( " : " ) < < Errors . ToString ( ) ;
}
2023-03-17 11:15:24 -04:00
const bool bShouldCache = EnumHasAllFlags ( CachingPolicy , ECachingPolicy : : CacheOnPull ) ;
TArray < FPushRequest > PayloadsToCache = RequestsCollection . OnPullCompleted ( * Backend , bShouldCache ) ;
if ( ! PayloadsToCache . IsEmpty ( ) )
{
2023-04-17 07:45:19 -04:00
const IVirtualizationBackend : : EPushFlags CacheOnPullFlags = bForceCachingOnPull ? IVirtualizationBackend : : EPushFlags : : Force
: IVirtualizationBackend : : EPushFlags : : None ;
CachePayloads ( PayloadsToCache , Backend , CacheOnPullFlags ) ;
2023-03-17 11:15:24 -04:00
}
// We can early out if there is no more requests to make
if ( RequestsCollection . IsWorkComplete ( ) )
{
break ;
}
2022-04-22 07:26:04 -04:00
}
2023-03-17 11:15:24 -04:00
if ( RequestsCollection . IsWorkComplete ( ) )
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
{
2023-03-17 11:15:24 -04:00
return ; // All payloads pulled
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use.
#rb Devin.Doucette
#jira UE-161599
#rnx
#preflight 6303c8d65a5d4e4624e7bf52
- There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this:
-- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file
-- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs
-- Running with the commandline option -VA-LazyInitConnections
-- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing)
--- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc.
- In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach.
-- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working.
-- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item.
- If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query
-- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple.
- Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it.
- If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item.
[CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
}
2024-02-01 10:00:35 -05:00
else if ( OnPayloadPullError ( RequestsCollection , BackendErrors ) = = ErrorHandlingResult : : AcceptFailedPayloads )
2022-04-22 07:26:04 -04:00
{
2024-02-01 10:00:35 -05:00
return ; // Some payloads failed to pull but we will let the calling code deal with that.
// This path should only be taken if legacy error handling is enabled.
2022-04-22 07:26:04 -04:00
}
2024-02-01 10:00:35 -05:00
// The user opted to retry pulling the payloads that failed.
2022-04-22 07:26:04 -04:00
}
}
2023-04-25 08:47:06 -04:00
void FVirtualizationManager : : PullDataFromBackend ( IVirtualizationBackend & Backend , TArrayView < FPullRequest > Requests , FText & OutErrors )
2021-06-03 04:35:17 -04:00
{
2022-10-21 22:27:40 -04:00
COOK_STAT ( FCookStats : : CallStats & Stats = Profiling : : GetPullStats ( Backend ) ) ;
COOK_STAT ( FCookStats : : FScopedStatsCounter Timer ( Stats ) ) ;
COOK_STAT ( Timer . TrackCyclesOnly ( ) ) ;
2024-01-26 11:09:38 -05:00
2023-04-25 08:47:06 -04:00
Backend . PullData ( Requests , IVirtualizationBackend : : EPullFlags : : None , OutErrors ) ;
2024-01-26 11:09:38 -05:00
2022-10-21 22:27:40 -04:00
# if ENABLE_COOK_STATS
const bool bIsInGameThread = IsInGameThread ( ) ;
2021-06-03 04:35:17 -04:00
2022-10-21 22:27:40 -04:00
for ( const FPullRequest & Request : Requests )
{
Timer . AddHit ( 0 ) ;
if ( Request . IsSuccess ( ) )
{
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ;
Stats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes , Request . GetPayload ( ) . GetCompressedSize ( ) , bIsInGameThread ) ;
}
}
# endif //ENABLE_COOK_STATS
}
2022-08-09 07:51:55 -04:00
2023-10-17 03:58:43 -04:00
FVirtualizationManager : : ErrorHandlingResult FVirtualizationManager : : OnPayloadPullError ( const FPullRequestCollection & Requests , FStringView BackendErrors ) const
2023-03-17 11:15:24 -04:00
{
if ( bUseLegacyErrorHandling )
{
return ErrorHandlingResult : : AcceptFailedPayloads ;
}
2023-10-17 03:58:43 -04:00
for ( const FIoHash & FailedPayload : Requests . GetFailedPayloads ( ) )
{
2024-01-26 11:09:38 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to pull payload '%s' " ) , * LexToString ( FailedPayload ) ) ;
2023-10-17 03:58:43 -04:00
}
2023-03-17 11:15:24 -04:00
static FCriticalSection CriticalSection ;
if ( CriticalSection . TryLock ( ) )
{
2023-03-20 07:40:06 -04:00
const FText Title ( LOCTEXT ( " VAPullTitle " , " Failed to pull virtualized data! " ) ) ;
2023-03-17 11:15:24 -04:00
2023-03-20 07:40:06 -04:00
FTextBuilder MsgBuilder ;
MsgBuilder . AppendLine ( LOCTEXT ( " VAPullMsgHeader " , " Failed to pull payload(s) from virtualization storage and allowing the editor to continue could corrupt data! " ) ) ;
2023-09-25 06:48:33 -04:00
2023-04-25 08:47:06 -04:00
if ( ! BackendErrors . IsEmpty ( ) )
{
MsgBuilder . AppendLine ( FString ( TEXT ( " " ) ) ) ;
MsgBuilder . AppendLine ( FString ( BackendErrors ) ) ;
}
2023-03-20 07:40:06 -04:00
if ( ! PullErrorAdditionalMsg . IsEmpty ( ) )
{
MsgBuilder . AppendLine ( FString ( TEXT ( " " ) ) ) ;
MsgBuilder . AppendLine ( PullErrorAdditionalMsg ) ;
}
2023-03-17 11:15:24 -04:00
2023-09-25 06:48:33 -04:00
// By default we should quit the process unless the user can opt to retry
EAppReturnType : : Type Result = EAppReturnType : : No ;
2023-03-20 07:40:06 -04:00
2023-09-25 06:48:33 -04:00
if ( Utils : : IsProcessInteractive ( ) )
{
MsgBuilder . AppendLine ( FString ( TEXT ( " " ) ) ) ;
MsgBuilder . AppendLine ( LOCTEXT ( " VAPullMsgYes " , " [Yes] Retry pulling the data " ) ) ;
MsgBuilder . AppendLine ( LOCTEXT ( " VAPullMsgNo " , " [No] Quit the editor " ) ) ;
2023-03-17 11:15:24 -04:00
2023-09-25 06:48:33 -04:00
const FText Message = MsgBuilder . ToText ( ) ;
2023-11-15 10:51:51 -05:00
FDisableUnattendedScriptGlobal DisableScope ;
2023-09-25 06:48:33 -04:00
Result = FMessageDialog : : Open ( EAppMsgType : : YesNo , EAppReturnType : : No , Message , Title ) ;
}
else
{
const FText Message = MsgBuilder . ToText ( ) ;
2024-01-26 11:09:38 -05:00
if ( ! ShouldRetryWhenUnattended ( ) | | UnattendedFailureMsgCount > = UnattendedRetryCount )
{
UE_LOG ( LogVirtualization , Error , TEXT ( " %s " ) , * Message . ToString ( ) ) ;
}
else
{
// Only log as a warning while we are retrying so that if we do recover out logging will
// not cause the running process to be considered a failure due to an error.
UE_LOG ( LogVirtualization , Warning , TEXT ( " Failed to pull payload(s) on attempt (%d/%d) " ) , UnattendedFailureMsgCount . load ( ) , UnattendedRetryCount ) ;
Result = EAppReturnType : : Yes ; // Attempt to retry the failed pull
if ( UnattendedRetryTimer > 0 )
{
UE_LOG ( LogVirtualization , Warning , TEXT ( " Waiting for %d seconds before trying to pull again... " ) , UnattendedRetryTimer ) ;
FPlatformProcess : : SleepNoStats ( static_cast < float > ( UnattendedRetryTimer ) ) ;
}
UnattendedFailureMsgCount + + ;
}
2023-09-25 06:48:33 -04:00
}
2023-03-17 11:15:24 -04:00
2024-02-01 10:00:35 -05:00
{
TArray < FAnalyticsEventAttribute > Attributes ;
Attributes . Add ( { TEXT ( " UserSelection " ) , Result = = EAppReturnType : : No ? TEXT ( " Quit " ) : TEXT ( " Retry " ) } ) ;
// Need to flush if we are going to quit to make sure that the analytics payloads are sent properly.
const EAnalyticsFlags Flags = Result = = EAppReturnType : : No ? EAnalyticsFlags : : Flush : EAnalyticsFlags : : None ;
GetAnalyticsRecordEvent ( ) . Broadcast ( TEXT ( " Editor.VA.PayloadPullError " ) , Attributes , Flags ) ;
}
2023-03-17 11:15:24 -04:00
if ( Result = = EAppReturnType : : No )
{
2023-09-25 06:48:33 -04:00
// Reporting a fatal error will print the callstack and initiate the crash handling system to be treated like a bug
// where as this error indicates an infrastructure failure or other connection issue which needs to be solved.
// So we will force the process to close after logging our errors instead.
2023-03-17 11:15:24 -04:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to pull payloads from persistent storage and the connection could not be re-established, exiting... " ) ) ;
GIsCriticalError = 1 ;
FPlatformMisc : : RequestExit ( true ) ;
}
CriticalSection . Unlock ( ) ;
}
else
{
2023-09-25 06:48:33 -04:00
// Since we failed to get the lock the error is being dealt with on another thread so we need to wait here until
// that thread has resolved the problem or terminated the process.
2023-03-17 11:15:24 -04:00
FScopeLock _ ( & CriticalSection ) ;
}
return ErrorHandlingResult : : Retry ;
}
2022-08-09 07:51:55 -04:00
bool FVirtualizationManager : : ShouldVirtualizeAsset ( const UObject * OwnerObject ) const
{
if ( OwnerObject = = nullptr )
{
return true ;
}
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 true ;
}
const FName ClassName = OwnerClass - > GetFName ( ) ;
return DisabledAssetTypes . Find ( ClassName ) = = nullptr ;
}
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 ;
}
2023-02-14 07:19:53 -05:00
// Do not virtualize engine content
if ( MountPointName . ToView ( ) = = TEXT ( " /Engine/ " ) | | FPaths : : IsUnderDirectory ( MountPointPath . ToString ( ) , FPaths : : EnginePluginsDir ( ) ) )
2021-11-07 23:43:01 -05:00
{
2023-02-14 07:19:53 -05:00
return false ;
2021-11-07 23:43:01 -05:00
}
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
}
2024-08-06 05:03:43 -04:00
bool FVirtualizationManager : : ShouldVirtualize ( FStringView 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
}
2024-01-26 11:09:38 -05:00
bool FVirtualizationManager : : ShouldRetryWhenUnattended ( ) const
{
return UnattendedRetryCount > 0 ;
}
2022-10-21 22:27:40 -04:00
void FVirtualizationManager : : BroadcastEvent ( TConstArrayView < FPullRequest > Requests , ENotification Event )
{
for ( const FPullRequest & Request : Requests )
{
GetNotificationEvent ( ) . Broadcast ( IVirtualizationSystem : : PullEndedNotification , Request . GetIdentifier ( ) ) ;
}
}
2023-01-11 14:28:01 -05:00
void FVirtualizationManager : : GatherAnalytics ( TArray < FAnalyticsEventAttribute > & Attributes ) const
{
2024-05-08 07:30:33 -04:00
Attributes . Reserve ( Attributes . Num ( ) + 10 ) ;
2024-05-14 07:59:24 -04:00
FPayloadActivityInfo PayloadActivityInfo = GetSystemStatistics ( ) ;
2024-05-08 07:30:33 -04:00
const FString BaseName = TEXT ( " Virtualization " ) ;
2023-01-11 14:28:01 -05:00
{
2024-05-08 07:30:33 -04:00
FString AttrName = BaseName + TEXT ( " _Enabled " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , IsEnabled ( ) ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Cache_TimeSpent " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Cache . CyclesSpent * FPlatformTime : : GetSecondsPerCycle ( ) ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Cache_PayloadCount " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Cache . PayloadCount ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Cache_TotalBytes " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Cache . TotalBytes ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Push_TimeSpent " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Push . CyclesSpent * FPlatformTime : : GetSecondsPerCycle ( ) ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Push_PayloadCount " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Push . PayloadCount ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Push_TotalBytes " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Push . TotalBytes ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Pull_TimeSpent " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Pull . CyclesSpent * FPlatformTime : : GetSecondsPerCycle ( ) ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Pull_PayloadCount " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Pull . PayloadCount ) ;
}
2023-01-11 14:28:01 -05:00
2024-05-08 07:30:33 -04:00
{
FString AttrName = BaseName + TEXT ( " _Pull_TotalBytes " ) ;
Attributes . Emplace ( MoveTemp ( AttrName ) , ( double ) PayloadActivityInfo . Pull . TotalBytes ) ;
2023-01-11 14:28:01 -05:00
}
}
2024-08-06 05:03:43 -04:00
EPayloadFilterReason FVirtualizationManager : : FixFilterFlags ( FStringView PackagePath , uint64 SizeOnDisk , EPayloadFilterReason CurrentFilterFlags )
{
// We only apply new filters if the payload does not curently have any as stored filtered reasons would be checked first before
// we ever got to this part of the virtualization process.
if ( CurrentFilterFlags = = EPayloadFilterReason : : None )
{
if ( static_cast < int64 > ( SizeOnDisk ) < MinPayloadLength )
{
return EPayloadFilterReason : : MinSize ;
}
if ( ! ShouldVirtualize ( PackagePath ) )
{
return EPayloadFilterReason : : Path ;
}
}
return CurrentFilterFlags ;
}
2023-05-11 06:35:58 -04:00
FString FVirtualizationManager : : GetConnectionHelpUrl ( )
{
return ConnectionHelpUrl ;
}
2021-04-30 08:14:54 -04:00
} // namespace UE::Virtualization
2022-04-22 08:55:44 -04:00
2022-10-21 22:27:40 -04:00
# undef UE_INLINE_ALLOCATION_COUNT
2022-12-08 21:22:12 -05:00
# undef LOCTEXT_NAMESPACE