You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#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]
288 lines
14 KiB
C++
288 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Compression/CompressedBuffer.h"
|
|
#include "HAL/CriticalSection.h"
|
|
#include "Logging/LogMacros.h"
|
|
#include "Templates/UniquePtr.h"
|
|
|
|
#include "Virtualization/VirtualizationSystem.h"
|
|
|
|
class IConsoleObject;
|
|
class FOutputDevice;
|
|
|
|
// When enabled it will be possible to query push requests to see if they are filtered without actually pushing
|
|
// anything. This is due to a bug with the filtering system, where a payload that should be filtered will be
|
|
// virtualized by our process if the payload is already stored in persistent storage. This hack allows us to
|
|
// find which payloads should be filtered based on size and package path, then prevent them from being automatically
|
|
// switched to being virtualized. Then when they are 'pushed' the usual filtering path will reject them.
|
|
// The plan is to move this filtering to be done when the package trailer is created, on package save, in which
|
|
// case everything will work just like asset filtering and will be much more robust. However we need to prevent
|
|
// some development branches from accidentally virtualizing packages which should not be virtualized so this hack
|
|
// is a quick way to get the problem fixed and unblock workflows while the better fix is worked on. This hack
|
|
// should be removed before 5.1 ships.
|
|
#define ENABLE_FILTERING_HACK 1
|
|
|
|
/**
|
|
* Configuring the backend hierarchy
|
|
*
|
|
* The [Core.ContentVirtualization] section can contain a string 'BackendGraph' which will set with the name of
|
|
* the backend graph, if not set then the default 'ContentVirtualizationBackendGraph_None' will be used instead.
|
|
* This value can also be overridden from the command line by using 'BackendGraph=FooBar' where FooBar is the
|
|
* name of the graph.
|
|
*
|
|
* The first entry in the graph to be parsed will be the 'Hierarchy' which describes which backends should be
|
|
* mounted and in which order. For example 'Hierarchy=(Entry=Foo, Entry=Bar)' which should mount two backends
|
|
* 'Foo' and 'Bar' in that order.
|
|
*
|
|
* Each referenced backend in the hierarchy will then require it's own entry in the graph where the key will be
|
|
* it's name in the hierarchy and the value a string describing how to set it up.
|
|
* The value must contain 'Type=X' where X is the name used to find the correct IVirtualizationBackendFactory
|
|
* to create the backend with.
|
|
* Once the backend is created then reset of the string will be passed to it, so that additional customization
|
|
* can be extracted. Depending on the backend implementation these values may or may not be required.
|
|
*
|
|
* Example graph:
|
|
* [ContentVirtualizationBackendGraph_Example]
|
|
* Hierarchy=(Entry=MemoryCache, Entry=NetworkShare)
|
|
* MemoryCache=(Type=InMemory)
|
|
* NetworkShare=(Type=FileSystem, Path="\\path\to\somewhere")
|
|
*
|
|
* The graph is named 'ContentVirtualizationBackendGraph_Example'.
|
|
* The hierarchy contains two entries 'InMemory' and 'NetworkShare' to be mounted in that order
|
|
* MemoryCache creates a backend of type 'InMemory' and has no additional customization
|
|
* NetworkShare creates a backend of type 'FileSystem' and provides an additional path, the filesystem backend would
|
|
* fatal error without this value.
|
|
*/
|
|
|
|
/**
|
|
* Filtering
|
|
*
|
|
* When pushing a payload it can be filtered based on the path of the package it belongs to. The filtering options
|
|
* are set up via the config files.
|
|
* Note that this only affects pushing a payload, if the filtering for a project is changed to exclude a package that
|
|
* is already virtualized it will still be able to pull it's payloads as needed but will store them locally in the
|
|
* package the next time that it is saved.
|
|
* @see ShouldVirtualizePackage or ShouldVirtualize for implementation details.
|
|
*
|
|
* Basic Setup:
|
|
*
|
|
* [Core.ContentVirtualization]
|
|
* FilterMode=OptIn/OptOut When 'OptIn' payloads will be virtualized by default, when 'OptOut' they will not be virtualized by default
|
|
* FilterEngineContent=True/False When true any payload from a package under Engine/Content/.. will be excluded from virtualization
|
|
* FilterEnginePluginContent=True/False When true any payload from a package under Engine/Plugins/../Content/.. will be excluded from virtualization
|
|
*
|
|
* PackagePath Setup:
|
|
*
|
|
* In addition to the default filtering mode set above, payloads stored in packages can be filtered based on the
|
|
* package path. This allows a package to be including in the virtualization process or excluded from it.
|
|
*
|
|
* Note that these paths will be stored in the ini files under the Saved directory. To remove a path make sure to
|
|
* use the - syntax to remove the entry from the array, rather than removing the line itself. Otherwise it will
|
|
* persist until the saved config file has been reset.
|
|
*
|
|
* [/Script/Virtualization.VirtualizationFilterSettings]
|
|
* +ExcludePackagePaths="/MountPoint/PathToExclude/" Excludes any package found under '/MountPoint/PathToExclude/' from the virtualization process
|
|
* +ExcludePackagePaths="/MountPoint/PathTo/ThePackageToExclude" Excludes the specific package '/MountPoint/PathTo/ThePackageToExclude' from the virtualization process
|
|
* +IncludePackagePaths="/MountPoint/PathToInclude/" Includes any package found under '/MountPoint/PathToInclude/' in the virtualization process
|
|
* +IncludePackagePaths="/MountPoint/PathTo/ThePackageToInclude" Includes the specific package '/MountPoint/PathTo/ThePackageToInclude' in the virtualization process
|
|
*/
|
|
|
|
namespace UE::Virtualization
|
|
{
|
|
class IVirtualizationBackend;
|
|
class IVirtualizationBackendFactory;
|
|
|
|
/** The default mode of filtering to use with package paths that do not match entries in UVirtualizationFilterSettings */
|
|
enum class EPackageFilterMode : uint8
|
|
{
|
|
/** Packages will be virtualized by default and must be opted out by the use of UVirtualizationFilterSettings::ExcludePackagePaths */
|
|
OptOut,
|
|
/** Packages will not be virtualized by default and must be opted in by the user of UVirtualizationFilterSettings::IncludePackagePaths */
|
|
OptIn
|
|
};
|
|
|
|
/** Attempt to convert a string buffer to EPackageFilterMode */
|
|
bool LexTryParseString(EPackageFilterMode& OutValue, FStringView Buffer);
|
|
|
|
/** This is used as a wrapper around the various potential back end implementations.
|
|
The calling code shouldn't need to care about which back ends are actually in use. */
|
|
class FVirtualizationManager final : public IVirtualizationSystem
|
|
{
|
|
public:
|
|
using FRegistedFactories = TMap<FName, IVirtualizationBackendFactory*>;
|
|
using FBackendArray = TArray<IVirtualizationBackend*>;
|
|
|
|
FVirtualizationManager();
|
|
virtual ~FVirtualizationManager();
|
|
|
|
#if ENABLE_FILTERING_HACK
|
|
void FilterRequests(TArray<FPushRequest>& Requests) const;
|
|
#endif //ENABLE_FILTERING_HACK
|
|
|
|
private:
|
|
/* IVirtualizationSystem implementation */
|
|
|
|
virtual bool Initialize(const FInitParams& InitParams) override;
|
|
|
|
virtual bool IsEnabled() const override;
|
|
virtual bool IsPushingEnabled(EStorageType StorageType) const override;
|
|
|
|
virtual bool IsDisabledForObject(const UObject* Owner) const override;
|
|
|
|
virtual bool PushData(const FIoHash& Id, const FCompressedBuffer& Payload, EStorageType StorageType, const FString& Context) override;
|
|
virtual bool PushData(TArrayView<FPushRequest> Requests, EStorageType StorageType) override;
|
|
|
|
virtual FCompressedBuffer PullData(const FIoHash& Id) override;
|
|
|
|
virtual EQueryResult QueryPayloadStatuses(TArrayView<const FIoHash> Ids, EStorageType StorageType, TArray<EPayloadStatus>& OutStatuses) override;
|
|
|
|
virtual bool TryVirtualizePackages(const TArray<FString>& FilesToVirtualize, TArray<FText>& OutDescriptionTags, TArray<FText>& OutErrors) override;
|
|
virtual bool TryRehydratePackages(const TArray<FString>& Packages, TArray<FText>& OutErrors) override;
|
|
|
|
virtual void DumpStats() const override;
|
|
|
|
virtual FPayloadActivityInfo GetAccumualtedPayloadActivityInfo() const override;
|
|
|
|
virtual void GetPayloadActivityInfo( GetPayloadActivityInfoFuncRef ) const override;
|
|
|
|
virtual FOnNotification& GetNotificationEvent() override
|
|
{
|
|
return NotificationEvent;
|
|
}
|
|
|
|
private:
|
|
|
|
void ApplySettingsFromConfigFiles(const FConfigFile& ConfigFile);
|
|
void ApplyDebugSettingsFromFromCmdline();
|
|
|
|
void RegisterConsoleCommands();
|
|
|
|
void OnUpdateDebugMissBackendsFromConsole(const TArray<FString>& Args, FOutputDevice& OutputDevice);
|
|
void OnUpdateDebugMissChanceFromConsole(const TArray<FString>& Args, FOutputDevice& OutputDevice);
|
|
void OnUpdateDebugMissCountFromConsole(const TArray<FString>& Args, FOutputDevice& OutputDevice);
|
|
|
|
void UpdateBackendDebugState();
|
|
|
|
bool ShouldDebugDisablePulling(FStringView BackendConfigName) const;
|
|
bool ShouldDebugFailPulling();
|
|
|
|
void MountBackends(const FConfigFile& ConfigFile);
|
|
void ParseHierarchy(const FConfigFile& ConfigFile, const TCHAR* GraphName, const TCHAR* HierarchyKey, const FRegistedFactories& FactoryLookupTable, FBackendArray& PushArray);
|
|
bool CreateBackend(const FConfigFile& ConfigFile, const TCHAR* GraphName, const FString& ConfigEntryName, const FRegistedFactories& FactoryLookupTable, FBackendArray& PushArray);
|
|
|
|
void AddBackend(TUniquePtr<IVirtualizationBackend> Backend, FBackendArray& PushArray);
|
|
|
|
void CachePayload(const FIoHash& Id, const FCompressedBuffer& Payload, const IVirtualizationBackend* BackendSource);
|
|
|
|
bool TryCacheDataToBackend(IVirtualizationBackend& Backend, const FIoHash& Id, const FCompressedBuffer& Payload);
|
|
bool TryPushDataToBackend(IVirtualizationBackend& Backend, TArrayView<FPushRequest> Requests);
|
|
|
|
FCompressedBuffer PullDataFromAllBackends(const FIoHash& Id);
|
|
FCompressedBuffer PullDataFromBackend(IVirtualizationBackend& Backend, const FIoHash& Id);
|
|
|
|
/**
|
|
* Determines if a package path should be virtualized or not based on any exclusion/inclusion patterns
|
|
* that might have been set in UVirtualizationFilterSettings.
|
|
* If the path does not match any pattern set in UVirtualizationFilterSettings then use the default
|
|
* FilterMode to determine if the payload should be virtualized or not.
|
|
*
|
|
* @param PackagePath The path of the package to check. This can be empty which would indicate that
|
|
* a payload is not owned by a specific package.
|
|
* @return True if the package should be virtualized and false if the package path is
|
|
* excluded by the projects current filter set up.
|
|
*/
|
|
bool ShouldVirtualizePackage(const FPackagePath& PackagePath) const;
|
|
|
|
/**
|
|
* Determines if a package should be virtualized or not based on the given content.
|
|
* If the context can be turned into a package path then ::ShouldVirtualizePackage
|
|
* will be used instead.
|
|
* If the context is not a package path then we use the default FilterMode to determine
|
|
* if the payload should be virtualized or not.
|
|
*
|
|
* @return True if the context should be virtualized and false if not.
|
|
*/
|
|
bool ShouldVirtualize(const FString& Context) const;
|
|
|
|
/** Determines if the default filtering behavior is to virtualize a payload or not */
|
|
bool ShouldVirtualizeAsDefault() const;
|
|
|
|
/** Are payloads allowed to be virtualized. Defaults to true. */
|
|
bool bEnablePayloadPushing;
|
|
|
|
/** Should payloads be cached locally after being pulled from persistent storage? Defaults to true. */
|
|
bool bEnableCacheAfterPull;
|
|
|
|
/** The minimum length for a payload to be considered for virtualization. Defaults to 0 bytes. */
|
|
int64 MinPayloadLength;
|
|
|
|
/** The name of the backend graph to load from the config ini file that will describe the backend hierarchy */
|
|
FString BackendGraphName;
|
|
|
|
/** The default filtering mode to apply if a payload is not matched with an option in UVirtualizationFilterSettings */
|
|
EPackageFilterMode FilteringMode = EPackageFilterMode::OptOut;
|
|
|
|
/** Should payloads in engine content packages before filtered out and never virtualized */
|
|
bool bFilterEngineContent;
|
|
|
|
/** Should payloads in engine plugin content packages before filtered out and never virtualized */
|
|
bool bFilterEnginePluginContent;
|
|
|
|
/** The name of the current project */
|
|
FString ProjectName;
|
|
|
|
/** The names of all asset types that should not virtualize. See @IsDisabledForObject */
|
|
TSet<FName> DisabledAssetTypes;
|
|
|
|
/** All of the backends that were mounted during graph creation */
|
|
TArray<TUniquePtr<IVirtualizationBackend>> AllBackends;
|
|
|
|
/** Backends used for caching operations (must support push operations). */
|
|
FBackendArray LocalCachableBackends;
|
|
|
|
/** Backends used for persistent storage operations (must support push operations). */
|
|
FBackendArray PersistentStorageBackends;
|
|
|
|
/**
|
|
* The hierarchy of backends to pull from, this is assumed to be ordered from fastest to slowest
|
|
* and can contain a mixture of local cacheable and persistent backends
|
|
*/
|
|
FBackendArray PullEnabledBackends;
|
|
|
|
/** Our notification Event */
|
|
FOnNotification NotificationEvent;
|
|
|
|
// Members after this point at used for debugging operations only!
|
|
|
|
struct FDebugValues
|
|
{
|
|
/** All of the console commands/variables that we register, so they can be unregistered when the manager is destroyed */
|
|
TArray<IConsoleObject*> ConsoleObjects;
|
|
|
|
/** The critical section used to force single threaded access if bForceSingleThreaded is true */
|
|
FCriticalSection ForceSingleThreadedCS;
|
|
|
|
/** When enabled all public operations will be performed as single threaded */
|
|
bool bSingleThreaded = false;
|
|
|
|
/**
|
|
* When enabled we will immediately 'pull' each payload after it has been pushed to either local or persistent
|
|
* storage and compare it to the original payload source to make sure that it was uploaded correctly
|
|
*/
|
|
bool bValidateAfterPush = false;
|
|
|
|
/** Array of backend names that should have their pull operation disabled */
|
|
TArray<FString> MissBackends;
|
|
|
|
/** The chance that a payload pull can just 'fail' to allow for testing */
|
|
float MissChance;
|
|
|
|
/** The number of upcoming payload pulls that should be failed */
|
|
std::atomic<int32> MissCount = 0;
|
|
} DebugValues;
|
|
};
|
|
|
|
} // namespace UE::Virtualization
|