Files
UnrealEngineUWP/Engine/Source/Developer/DerivedDataCache/Private/DerivedDataBackends.cpp

881 lines
29 KiB
C++
Raw Normal View History

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Core.h"
#include "SecureHash.h"
#include "DerivedDataBackendInterface.h"
#include "DerivedDataCacheInterface.h"
#include "MemoryDerivedDataBackend.h"
#include "DerivedDataBackendAsyncPutWrapper.h"
#include "PakFileDerivedDataBackend.h"
#include "HierarchicalDerivedDataBackend.h"
#include "DerivedDataLimitKeyLengthWrapper.h"
#include "DerivedDataBackendCorruptionWrapper.h"
#include "DerivedDataBackendVerifyWrapper.h"
#include "DerivedDataUtilsInterface.h"
#include "Misc/EngineBuildSettings.h"
Copying //UE4/Orion-Staging to //UE4/Main (Origin //Orion/Dev-General @ 2870388) #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2870336 on 2016/02/17 by Marc.Audy Continued splitting up Orion Build * Restructure from platform based MakeBuild steps in to a PS4, Server, and Windows Client MakeBuild * Cook server data only once for both Windows and Linux (windows reuses Linux server data) * Split compilation of Win64 Client and Server such that MakeBuild_Server only builds Server and MakeBuild_WindowsClient only builds Client #jira UEB-580 #rb Ben.Marsh #tests Preflight and generated Windows Client and Server work to play game Change 2870026 on 2016/02/17 by Wes.Hunt Don't allow array shrinking when removing the corruption wrapper trailer. #rb none Updating CIS Counter Change 2869725 on 2016/02/17 by Dmitry.Rekman More analytics and QoS stats added for 0.19. #rb none #tests Ran Windows client and Linux server on compatible content. Change 2869705 on 2016/02/16 by Ryan.Gerleve Fix replicated properties and call RepNotifies of startup actors when scrubbing in replays. This is the engine support for fixing OR-6817, towers not respawning when rewinding replays. #rb john.pollard #tests golden path, replays, ps4 nomcp Change 2869644 on 2016/02/16 by Jason.Bestimt #ORION_DEV - Merge MAIN (0.18) at CL# 2869635 #Tests:none #RB:none Change 2869586 on 2016/02/16 by Marcus.Wassmer Fix texturestreaming RHI flushes. #rb none #test goldenpath #codereview Gil.Gribb Change 2869279 on 2016/02/16 by Lukasz.Furman fixed minion hit reaction directions #orion OR-13953 #rb Mieszko.Zielinski #tests PIE: hit minions with various abilities from different angles, checked velocity of death particles when killed by abilities and towers #codereview Dan.Youhon Change 2869277 on 2016/02/16 by Wes.Hunt During cook, when a package is not ready to save, actually early out of the saving code. Saves somewhere in the 130s to 200s range for cooks. #rb daniel.lamb #tests local windows cooks, preflight PS4 cooks Change 2869132 on 2016/02/16 by Mieszko.Zielinski Added a function to AISenseConfig allowing native-code MaxAge configuration #UE4 #rb Lukasz.Furman #test none required Change 2868981 on 2016/02/16 by Wes.Hunt remove -LogCookStats cmdline check, always log cook stats. -SendCookAnalytics flag is still used. This was requested by NickP. #rb none #tests local windows cooks Change 2868975 on 2016/02/16 by Wes.Hunt Don't submit DDC usage stats for zero-sized events. #rb none #tests local windows cook Change 2868956 on 2016/02/16 by Jason.Bestimt #ORION_DEV - Merge MAIN (0.18) at CL# 2868926 #RB:none #Tests:none Change 2868889 on 2016/02/16 by Max.Chen Sequencer: Only allow transport control binding when editing level editor sequencers. #rb none #tests none Change 2868663 on 2016/02/16 by David.Ratti downgrade warning to display #rb none #tests compile Change 2868624 on 2016/02/16 by Marcus.Wassmer Re-Enable Defrag validation for devgeneral #rb none #test none Change 2868493 on 2016/02/16 by Benn.Gallagher Added a few more stats to morph target updates to try and narrow down hitches #rb Bruce.Nesbit #tests pie, -game Win64 Change 2868445 on 2016/02/16 by Dmitry.Rekman Linux: report crashes due to stack overflow (OR-14519). - Reserve memory for alternative stack for signal handlers. Adds about 128KB memory per thread. - Force process spawning to use vfork() when no pipes are needed. - Ignore all signals except explicitly handled. - Prevent signals from being raised while another one is handled. - Added "debug threadrecurse" and "debug threadstackoverflow" to test that. [CL 2873763 by Andrew Grant in Main branch]
2016-02-19 12:03:17 -05:00
#include "DerivedDataCacheUsageStats.h"
DEFINE_LOG_CATEGORY(LogDerivedDataCache);
#define MAX_BACKEND_KEY_LENGTH (120)
#define LOCTEXT_NAMESPACE "DerivedDataBackendGraph"
FDerivedDataBackendInterface* CreateFileSystemDerivedDataBackend(const TCHAR* CacheDirectory, bool bForceReadOnly = false, bool bTouchFiles = false, bool bPurgeTransient = false, bool bDeleteOldFiles = false, int32 InDaysToDeleteUnusedFiles = 60, int32 InMaxNumFoldersToCheck = -1, int32 InMaxContinuousFileChecks = -1);
/**
* This class is used to create a singleton that represents the derived data cache hierarchy and all of the wrappers necessary
* ideally this would be data driven and the backends would be plugins...
**/
class FDerivedDataBackendGraph : public FDerivedDataBackend
{
public:
/**
* constructor, builds the cache tree
**/
FDerivedDataBackendGraph()
: RootCache(NULL)
, BootCache(NULL)
, WritePakCache(NULL)
, AsyncPutWrapper(NULL)
, KeyLengthWrapper(NULL)
, HierarchicalWrapper(NULL)
, MountPakCommand(
TEXT( "DDC.MountPak" ),
*LOCTEXT("CommandText_DDCMountPak", "Mounts read-only pak file").ToString(),
FConsoleCommandWithArgsDelegate::CreateRaw( this, &FDerivedDataBackendGraph::MountPakCommandHandler ) )
, UnountPakCommand(
TEXT( "DDC.UnmountPak" ),
*LOCTEXT("CommandText_DDCUnmountPak", "Unmounts read-only pak file").ToString(),
FConsoleCommandWithArgsDelegate::CreateRaw( this, &FDerivedDataBackendGraph::UnmountPakCommandHandler ) )
{
check(IsInGameThread()); // we pretty much need this to be initialized from the main thread...it uses GConfig, etc
check(GConfig && GConfig->IsReadyForUse());
RootCache = NULL;
TMap<FString, FDerivedDataBackendInterface*> ParsedNodes;
// Create the graph using ini settings
if( FParse::Value( FCommandLine::Get(), TEXT("-DDC="), GraphName ) )
{
if( GraphName.Len() > 0 )
{
RootCache = ParseNode( TEXT("Root"), GEngineIni, *GraphName, ParsedNodes );
}
if( RootCache == NULL )
{
// Remove references to any backend instances that might have been created
ParsedNodes.Empty();
DestroyCreatedBackends();
UE_LOG( LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendGraph: Unable to create backend graph using the specified graph settings (%s). Reverting to default."), *GraphName );
}
}
if( !RootCache )
{
// Use default graph
GraphName = FApp::IsEngineInstalled() ? TEXT("InstalledDerivedDataBackendGraph") : TEXT("DerivedDataBackendGraph");
FString Entry;
if( !GConfig->GetString( *GraphName, TEXT("Root"), Entry, GEngineIni ) || !Entry.Len())
{
UE_LOG( LogDerivedDataCache, Fatal, TEXT("Unable to create backend graph using the default graph settings (%s) ini=%s."), *GraphName, *GEngineIni );
}
RootCache = ParseNode( TEXT("Root"), GEngineIni, *GraphName, ParsedNodes );
}
check(RootCache);
// Make sure AsyncPutWrapper and KeyLengthWrapper are created
if( !AsyncPutWrapper )
{
AsyncPutWrapper = new FDerivedDataBackendAsyncPutWrapper( RootCache, true );
check(AsyncPutWrapper);
CreatedBackends.Add( AsyncPutWrapper );
RootCache = AsyncPutWrapper;
}
if ( !KeyLengthWrapper )
{
KeyLengthWrapper = new FDerivedDataLimitKeyLengthWrapper( RootCache, MAX_BACKEND_KEY_LENGTH );
check(KeyLengthWrapper);
CreatedBackends.Add( KeyLengthWrapper );
RootCache = KeyLengthWrapper;
}
}
/**
* Helper function to get the value of parsed bool as the return value
**/
bool GetParsedBool( const TCHAR* Stream, const TCHAR* Match ) const
{
bool bValue = 0;
FParse::Bool( Stream, Match, bValue );
return bValue;
}
/**
* Parses backend graph node from ini settings
*
* @param NodeName Name of the node to parse
* @param IniFilename Ini filename
* @param IniSection Section in the ini file containing the graph definition
* @param InParsedNodes Map of parsed nodes and their names to be able to find already parsed nodes
* @return Derived data backend interface instance created from ini settings
*/
FDerivedDataBackendInterface* ParseNode( const TCHAR* NodeName, const FString& IniFilename, const TCHAR* IniSection, TMap<FString, FDerivedDataBackendInterface*>& InParsedNodes )
{
FDerivedDataBackendInterface* ParsedNode = NULL;
FString Entry;
if( GConfig->GetString( IniSection, NodeName, Entry, IniFilename ) )
{
// Trim whitespace at the beginning.
Entry = Entry.Trim();
// Remove brackets.
Entry.RemoveFromStart(TEXT("("));
Entry.RemoveFromEnd(TEXT(")"));
FString NodeType;
if( FParse::Value( *Entry, TEXT("Type="), NodeType ) )
{
if( NodeType == TEXT("FileSystem") )
{
ParsedNode = ParseDataCache( NodeName, *Entry );
}
else if( NodeType == TEXT("Boot") )
{
if( BootCache == NULL )
{
BootCache = ParseBootCache( NodeName, *Entry, BootCacheFilename );
ParsedNode = BootCache;
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendGraph: Unable to create %s Boot cache because only one Boot cache node is supported."), *NodeName );
}
}
else if( NodeType == TEXT("Memory") )
{
ParsedNode = ParseMemoryCache( NodeName, *Entry );
}
else if( NodeType == TEXT("Hierarchical") )
{
ParsedNode = ParseHierarchicalCache( NodeName, *Entry, IniFilename, IniSection, InParsedNodes );
}
else if( NodeType == TEXT("KeyLength") )
{
if( KeyLengthWrapper == NULL )
{
KeyLengthWrapper = ParseKeyLength( NodeName, *Entry, IniFilename, IniSection, InParsedNodes );
ParsedNode = KeyLengthWrapper;
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendGraph: Unable to create %s KeyLengthWrapper because only one KeyLength node is supported."), *NodeName );
}
}
else if( NodeType == TEXT("AsyncPut") )
{
if( AsyncPutWrapper == NULL )
{
AsyncPutWrapper = ParseAsyncPut( NodeName, *Entry, IniFilename, IniSection, InParsedNodes );
ParsedNode = AsyncPutWrapper;
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendGraph: Unable to create %s AsyncPutWrapper because only one AsyncPutWrapper node is supported."), *NodeName );
}
}
else if( NodeType == TEXT("Verify") )
{
ParsedNode = ParseVerify( NodeName, *Entry, IniFilename, IniSection, InParsedNodes );
}
else if( NodeType == TEXT("ReadPak") )
{
ParsedNode = ParsePak( NodeName, *Entry, false );
}
else if( NodeType == TEXT("WritePak") )
{
ParsedNode = ParsePak( NodeName, *Entry, true );
}
}
}
if( ParsedNode )
{
// Store this node so that we don't require any order of adding backend nodes
InParsedNodes.Add( NodeName, ParsedNode );
// Keep references to all created nodes.
CreatedBackends.AddUnique( ParsedNode );
}
return ParsedNode;
}
/**
* Creates Read/write Pak file interface from ini settings
*
* @param NodeName Node name
* @param Entry Node definition
* @param bWriting true to create pak interface for writing
* @return Pak file data backend interface instance or NULL if unsuccessful
*/
FDerivedDataBackendInterface* ParsePak( const TCHAR* NodeName, const TCHAR* Entry, const bool bWriting )
{
FDerivedDataBackendInterface* PakNode = NULL;
FString PakFilename;
FParse::Value( Entry, TEXT("Filename="), PakFilename );
bool bCompressed = GetParsedBool(Entry, TEXT("Compressed="));
if( !PakFilename.Len() )
{
UE_LOG( LogDerivedDataCache, Log, TEXT("FDerivedDataBackendGraph: %s pak cache Filename not found in *engine.ini, will not use a pak cache."), NodeName );
}
else
{
// now add the pak read cache (if any) to the front of the cache hierarchy
if ( bWriting )
{
FGuid Temp = FGuid::NewGuid();
ReadPakFilename = PakFilename;
WritePakFilename = PakFilename + TEXT(".") + Temp.ToString();
WritePakCache = bCompressed? new FCompressedPakFileDerivedDataBackend( *WritePakFilename, true ) : new FPakFileDerivedDataBackend( *WritePakFilename, true );
PakNode = WritePakCache;
}
else
{
bool bReadPak = FPlatformFileManager::Get().GetPlatformFile().FileExists( *PakFilename );
if( bReadPak )
{
FPakFileDerivedDataBackend* ReadPak = bCompressed? new FCompressedPakFileDerivedDataBackend( *PakFilename, false ) : new FPakFileDerivedDataBackend( *PakFilename, false );
ReadPakFilename = PakFilename;
PakNode = ReadPak;
ReadPakCache.Add(ReadPak);
}
else
{
UE_LOG( LogDerivedDataCache, Log, TEXT("FDerivedDataBackendGraph: %s pak cache file %s not found, will not use a pak cache."), NodeName, *PakFilename );
}
}
}
return PakNode;
}
/**
* Creates Verify wrapper interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @param IniFilename ini filename.
* @param IniSection ini section containing graph definition
* @param InParsedNodes map of nodes and their names which have already been parsed
* @return Verify wrapper backend interface instance or NULL if unsuccessful
*/
FDerivedDataBackendInterface* ParseVerify( const TCHAR* NodeName, const TCHAR* Entry, const FString& IniFilename, const TCHAR* IniSection, TMap<FString, FDerivedDataBackendInterface*>& InParsedNodes )
{
FDerivedDataBackendInterface* InnerNode = NULL;
FString InnerName;
if( FParse::Value( Entry, TEXT("Inner="), InnerName ) )
{
FDerivedDataBackendInterface** ParsedInnerNode = InParsedNodes.Find( InnerName );
if( ParsedInnerNode )
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Inner node %s for Verify node %s already exists. Nodes can only be used once."), *InnerName, NodeName );
return NULL;
}
else
{
InnerNode = ParseNode( *InnerName, IniFilename, IniSection, InParsedNodes );
}
}
FDerivedDataBackendInterface* VerifyNode = NULL;
if( InnerNode )
{
IFileManager::Get().DeleteDirectory(*(FPaths::GameSavedDir() / TEXT("VerifyDDC/")), false, true);
const bool bFix = GetParsedBool( Entry, TEXT("Fix=") );
VerifyNode = new FDerivedDataBackendVerifyWrapper( InnerNode, bFix );
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Unable to find inner node %s for Verify node %s. Verify node will not be created."), *InnerName, NodeName );
}
return VerifyNode;
}
/**
* Creates AsyncPut wrapper interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @param IniFilename ini filename.
* @param IniSection ini section containing graph definition
* @param InParsedNodes map of nodes and their names which have already been parsed
* @return AsyncPut wrapper backend interface instance or NULL if unsuccessfull
*/
FDerivedDataBackendInterface* ParseAsyncPut( const TCHAR* NodeName, const TCHAR* Entry, const FString& IniFilename, const TCHAR* IniSection, TMap<FString, FDerivedDataBackendInterface*>& InParsedNodes )
{
FDerivedDataBackendInterface* InnerNode = NULL;
FString InnerName;
if( FParse::Value( Entry, TEXT("Inner="), InnerName ) )
{
FDerivedDataBackendInterface** ParsedInnerNode = InParsedNodes.Find( InnerName );
if( ParsedInnerNode )
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Inner node %s for AsyncPut node %s already exists. Nodes can only be used once."), *InnerName, NodeName );
return NULL;
}
else
{
InnerNode = ParseNode( *InnerName, IniFilename, IniSection, InParsedNodes );
}
}
FDerivedDataBackendInterface* AsyncNode = NULL;
if( InnerNode )
{
AsyncNode = new FDerivedDataBackendAsyncPutWrapper( InnerNode, true );
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Unable to find inner node %s for AsyncPut node %s. AsyncPut node will not be created."), *InnerName, NodeName );
}
return AsyncNode;
}
/**
* Creates KeyLength wrapper interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @param IniFilename ini filename.
* @param IniSection ini section containing graph definition
* @param InParsedNodes map of nodes and their names which have already been parsed
* @return KeyLength wrapper backend interface instance or NULL if unsuccessfull
*/
FDerivedDataBackendInterface* ParseKeyLength( const TCHAR* NodeName, const TCHAR* Entry, const FString& IniFilename, const TCHAR* IniSection, TMap<FString, FDerivedDataBackendInterface*>& InParsedNodes )
{
FDerivedDataBackendInterface* InnerNode = NULL;
FString InnerName;
if( FParse::Value( Entry, TEXT("Inner="), InnerName ) )
{
FDerivedDataBackendInterface** ParsedInnerNode = InParsedNodes.Find( InnerName );
if( ParsedInnerNode )
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Inner node %s for KeyLength node %s already exists. Nodes can only be used once."), *InnerName, NodeName );
return NULL;
}
else
{
InnerNode = ParseNode( *InnerName, IniFilename, IniSection, InParsedNodes );
}
}
FDerivedDataBackendInterface* KeyLengthNode = NULL;
if( InnerNode )
{
int32 KeyLength = MAX_BACKEND_KEY_LENGTH;
FParse::Value( Entry, TEXT("Length="), KeyLength );
KeyLength = FMath::Clamp( KeyLength, 0, MAX_BACKEND_KEY_LENGTH );
KeyLengthNode = new FDerivedDataLimitKeyLengthWrapper( InnerNode, KeyLength );
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Unable to find inner node %s for KeyLength node %s. KeyLength node will not be created."), *InnerName, NodeName );
}
return KeyLengthNode;
}
/**
* Creates Hierarchical interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @param IniFilename ini filename.
* @param IniSection ini section containing graph definition
* @param InParsedNodes map of nodes and their names which have already been parsed
* @return Hierarchical backend interface instance or NULL if unsuccessfull
*/
FDerivedDataBackendInterface* ParseHierarchicalCache( const TCHAR* NodeName, const TCHAR* Entry, const FString& IniFilename, const TCHAR* IniSection, TMap<FString, FDerivedDataBackendInterface*>& InParsedNodes )
{
const TCHAR* InnerMatch = TEXT("Inner=");
const int32 InnerMatchLength = FCString::Strlen( InnerMatch );
TArray< FDerivedDataBackendInterface* > InnerNodes;
FString InnerName;
while ( FParse::Value( Entry, InnerMatch, InnerName ) )
{
// Check if the child has already been parsed
FDerivedDataBackendInterface** ParsedInnerNode = InParsedNodes.Find( InnerName );
if( ParsedInnerNode )
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Inner node %s for hierarchical node %s already exists. Nodes can only be used once."), *InnerName, NodeName );
}
else
{
FDerivedDataBackendInterface* InnerNode = ParseNode( *InnerName, IniFilename, IniSection, InParsedNodes );
if( InnerNode )
{
InnerNodes.Add( InnerNode );
}
else
{
UE_LOG( LogDerivedDataCache, Log, TEXT("Unable to find inner node %s for hierarchical cache %s."), *InnerName, NodeName );
}
}
// Move the Entry pointer forward so that we can find more children
Entry = FCString::Strfind( Entry, InnerMatch );
Entry += InnerMatchLength;
}
FDerivedDataBackendInterface* Hierarchy = NULL;
if( InnerNodes.Num() > 1 )
{
FHierarchicalDerivedDataBackend* HierarchyBackend = new FHierarchicalDerivedDataBackend( InnerNodes );
Hierarchy = HierarchyBackend;
if (HierarchicalWrapper == NULL)
{
HierarchicalWrapper = HierarchyBackend;
}
}
else if ( InnerNodes.Num() == 1 )
{
Hierarchy = InnerNodes[ 0 ];
Copying //UE4/Dev-Platform to //UE4/Main ========================== MAJOR FEATURES + CHANGES ========================== Change 2816560 on 2016/01/05 by Jeff.Campeau Remove duplicate CEF binaries Change 2835599 on 2016/01/20 by Lee.Clark PS4 - Added pragma optimization macros Change 2841103 on 2016/01/23 by Mark.Satterthwaite Integrate Git PR #1958: Fixed typo in EMetalFeatures enum #jira UE-25721 Change 2841369 on 2016/01/24 by Mark.Satterthwaite Fix for Metal crash due to attempt to set a null uniform & null SRV to a shader which requires both exist. #jira UE-25910 Change 2841795 on 2016/01/25 by Lee.Clark PS4 - MovieStreamer improvements * Use GPU for YUV conversion * Use new Software2 Decoder Change 2842261 on 2016/01/25 by Mark.Satterthwaite Fix some memory leaks. Change 2842831 on 2016/01/25 by Mark.Satterthwaite Metal implementation for RHIBlockUntilGPUIdle. Change 2842838 on 2016/01/25 by Mark.Satterthwaite When using parallel command contexts in Metal we must ensure that the FRingBuffer is still valid, which means some smart/weak pointers are in order. We should also ensure that functions that may return auto-released objects are appropriately wrapped with scoped autorelease pools. Texture creation failures should also be fatal as we never expect that to occur. Change 2842914 on 2016/01/25 by Mark.Satterthwaite Change assert in MetalTexture's format-shifting SRV constructor to enforce the Metal textureView limits: 1. No format shifting for MSAA color buffers. 2. No access to stencil in MSAA packed depth/stencil surface. This will allow Metal MSAA support to work on iOS when using separate depth & stencil textures since there's no format shifting involved there. #codereview peter.sauerbrei Change 2843028 on 2016/01/25 by Mark.Satterthwaite In Metal wwitch to blit on the correct context when copying out the stencil data into the stencil SRV copy. Change 2845531 on 2016/01/27 by Lee.Clark PS4 - Fix memory alignment for back buffers * Fix memory alignment for MapLargeBlock * Fix available direct memory tracking Change 2846491 on 2016/01/27 by Jeff.Campeau 2015 compile fixes for Orion Change 2847395 on 2016/01/28 by Mark.Satterthwaite Clear the stencil-SRV copy to 0 in Metal using a blit when created to avoid artefacts if used prior to the parent texture being rendered. #jira UE-25834 Change 2847419 on 2016/01/28 by Mark.Satterthwaite Apply the same fix to OpenGL's Stencil SRV logic as CL #2847395 applies to Metal. Change 2848093 on 2016/01/28 by Mark.Satterthwaite Cache parallel encoding Metal contexts & reuse them rather than creating a new one each time in order to massively improve parallel encoding performance. This required adding a reset function to Metal's internal state-cache which calls the CommandEncoder wrapper's reset so we don't accidently retain previous state. Change 2849469 on 2016/01/29 by Mark.Satterthwaite Defer render & compute command encoder construction to draw/dispatch etc to eliminate redundant encoders that then perform unnecessary driver & GPU synchronisation work. Currently Clear loadActions force an encoder even if it would then be empty as otherwise we see incorrect rendering. This needs to be tracked and optimised away too in order to achieve the same performance as D3D11. Change 2849820 on 2016/01/29 by Daniel.Lamb Fixed issue where a single DDC back end would not create a hierarchy. #codereview Peter.Sauerbrei Change 2850762 on 2016/02/01 by Jeff.Campeau System-wide critical section support for Xbox One Change 2850763 on 2016/02/01 by Jeff.Campeau Network and product config for Orion Change 2852459 on 2016/02/02 by Mark.Satterthwaite Temporarily disable the lazy render command-encoder construction while investigating why it turns some samples black in Metal SM5 mode. Change 2853947 on 2016/02/03 by Mark.Satterthwaite Fix some lazy encoder construction fallout which also means we don't need to recreate render encoder state when performing profiling - the next draw/clear will do that as required. Change 2854015 on 2016/02/03 by Mark.Satterthwaite Move Stencil SRV blitting into FMetalSurface::UpdateSRV called when binding the texture SRV instead of having it done immediately post-rendering. This should avoid paying for the blit when stencil SRV sampling is never used or multiple blits when render-encoders that write stencil are split up due to query buffer overflow or similar. The cost will be a blit per-bind instead which should be more predictable. Change 2854142 on 2016/02/03 by Mark.Satterthwaite Implemented GetTextureBaseRHI (brought over from Dev-Rendering CL #2853948) for Metal to avoid unnecessary virtual function call chain to resolve the FMetalSurface* from an RHI texture. Change 2854222 on 2016/02/03 by Mark.Satterthwaite Remove the uniform buffer resource caching from Metal to match Dev-Rendering CL #2853948. Change 2854246 on 2016/02/03 by Mark.Satterthwaite Removed the uniform buffer resource caching from OpenGLDrv & implemented GetTextureBaseRHI to avoid unnecessary virtual function calls to match Dev-Rendering CL #2853948. Change 2854279 on 2016/02/03 by Mark.Satterthwaite Remove direct access to the MTLCommandQueue, for parallel rendering to work we're going to need to do a bit of management that means its more sensible to keep it private. Change 2855524 on 2016/02/04 by Lee.Clark PS4 - Fix Grayscale SRGB support [CL 2898161 by Josh Adams in Main branch]
2016-03-07 20:55:29 -05:00
InnerNodes.Empty();
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Hierarchical cache %s has no inner backends and will not be created."), NodeName );
}
return Hierarchy;
}
/**
* Creates Filesystem data cache interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @return Filesystem data cache backend interface instance or NULL if unsuccessfull
*/
FDerivedDataBackendInterface* ParseDataCache( const TCHAR* NodeName, const TCHAR* Entry )
{
FDerivedDataBackendInterface* DataCache = NULL;
// Parse Path by default, it may be overwriten by EnvPathOverride
FString Path;
FParse::Value( Entry, TEXT("Path="), Path );
// Check the EnvPathOverride environment variable to allow persistent overriding of data cache path, eg for offsite workers.
FString EnvPathOverride;
if( FParse::Value( Entry, TEXT("EnvPathOverride="), EnvPathOverride ) )
{
TCHAR FilesystemCachePathEnv[256];
FPlatformMisc::GetEnvironmentVariable( *EnvPathOverride, FilesystemCachePathEnv, ARRAY_COUNT(FilesystemCachePathEnv) );
if( FilesystemCachePathEnv[0] )
{
Path = FilesystemCachePathEnv;
UE_LOG( LogDerivedDataCache, Log, TEXT("Found environment variable %s=%s"), *EnvPathOverride, *Path );
}
}
// Check if the Path is a real path or a special keyword
if (FEngineBuildSettings::IsInternalBuild())
{
auto DDCUtils = FModuleManager::LoadModulePtr< IDDCUtilsModuleInterface >("DDCUtils");
if (DDCUtils)
{
FString PathFromName = DDCUtils->GetSharedCachePath(Path);
if (!PathFromName.IsEmpty())
{
Path = PathFromName;
}
}
}
else if (Path.StartsWith(TEXT("?")))
{
Path = TEXT("");
}
if( !Path.Len() )
{
UE_LOG( LogDerivedDataCache, Log, TEXT("%s data cache path not found in *engine.ini, will not use an %s cache."), NodeName, NodeName );
}
Copying //UE4/Orion-Staging to //UE4/Main (Source: //Orion/Dev-General @ 2912736) #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2912513 on 2016/03/16 by David.Ratti attempted cook fix #rb none #tests compile Change 2912456 on 2016/03/16 by Zak.Middleton #ue4 - Move Replay client interpolation mode check to OnRegister(), out of TickComponent() so we don't check it constantly. #rb John.Pollard #tests Replays and MultiPIE vs AI Change 2912386 on 2016/03/16 by Ben.Marsh BuildGraph: Add quotes around custom build node lists, so we can support node names with spaces. Change 2912378 on 2016/03/16 by Ben.Marsh BuildGraph: Fix format of custom build arguments when running with buildgraph. Change 2912318 on 2016/03/16 by Marcus.Wassmer Fix mallocleakdetection false positives, and hash collision data corruption. #rb none #test leak finding on goldenpath Change 2912242 on 2016/03/16 by Lukasz.Furman CIS fix #rb none #tests none Change 2912239 on 2016/03/16 by Ben.Marsh UBT: Include the project "Build" folder in the generated project files. It's pretty common to want to edit configuration data that's stored there. #rb none #tests generated project files using UGS after making change Change 2912211 on 2016/03/16 by Ben.Marsh BuildFarm: Allow disabling the local DDC by setting the DDC override environment variable to "None". Testing performance of just using shared DDC for builders. #rb none #codereview Wes.Hunt #tests none Change 2912196 on 2016/03/16 by Mieszko.Zielinski Added a missing #pragma once to GameplayDebuggerCompat.h #Orion #rb Lukasz.Furman #test none Change 2912165 on 2016/03/16 by Lukasz.Furman new gameplay debugger and some replication fixes (disabled by default) uncommment debugger's setup line in FOrionGameModule::StartupModule to enable #orion #rb none #tests yes, a lot. #codereview Mieszko.Zielinski Change 2912065 on 2016/03/16 by Jason.Bestimt [AUTOMERGE] #ORION_MAIN - Copy of DevUI (POST MERGE) @ CL 2912016 #RB:none #Tests:none #CodeReview: matt.schembari -------- Integrated using branch //Orion/Main_to_//Orion/Dev-General of change#2912060 by Jason.Bestimt on 2016/03/16 15:32:56. Change 2912045 on 2016/03/16 by David.Ratti Add support for gameplay cues retrieiving the ability or gameplay effect level of the thing that instigated them. Also added level requirement settings in the addition particle system options in gameplay cues #rb DanY #tests PIE Change 2912030 on 2016/03/16 by Alex.Fennell Merging //Portal/Dev-LibCurl_update/Engine/Source/Runtime/Online/... to //Orion/Dev-General/Engine/Source/Runtime/Online/... support for cookies across curl easy handles in libcurl #TESTS: cookie support confirmed by using the http test targetting google.com #RB: david.nikdel #codereview: david.nikdel, jason.bestimt Change 2911870 on 2016/03/16 by Laurent.Delayen - Added FBranchingPointNotifyPayload used in AnimNotify and AnimNotifyState notifications. - FBranchingPointNotifyPayload includes MontageInstance InstanceID to uniquely identify which Montage it came from. - New notifications are backwards compatible with old. - Added bIsNativeBranchingPoint flag to notify classes to force them into branching points. #rb martin.wilson, frank.gigliotti #tests Kuro VS Rampage abilities in networked PIE Change 2911763 on 2016/03/16 by Nick.Atamas Prevents a re-entrancy crash caused by potentially complex thumbnail generators. In general, we should not be doing heavy lifting in the asset browser until the user has released their mouse. This is especially true when a user is clicking on the content browser tab to bring it to front for the first time. This is probably a good change regardless of the re-entrancy issue. #rb none #test Editor does not crash. #codereview Matt.Kuhlenschmidt Change 2911631 on 2016/03/16 by Dmitry.Rekman Fix AvgPing perfcounter being occasionally NaN (FORT-20523) - Prevent division by zero. #rb none [CL 2917701 by Andrew Grant in Main branch]
2016-03-21 21:11:39 -04:00
else if( Path == TEXT("None") )
{
UE_LOG( LogDerivedDataCache, Log, TEXT("Disabling %s data cache - path set to 'None'."), NodeName );
}
else
{
const bool bReadOnly = GetParsedBool( Entry, TEXT("ReadOnly=") );
const bool bClean = GetParsedBool( Entry, TEXT("Clean=") );
const bool bFlush = GetParsedBool( Entry, TEXT("Flush=") );
const bool bTouch = GetParsedBool( Entry, TEXT("Touch=") );
const bool bPurgeTransient = GetParsedBool( Entry, TEXT("PurgeTransient=") );
bool bDeleteUnused = true; // On by default
FParse::Bool( Entry, TEXT("DeleteUnused="), bDeleteUnused );
int32 UnusedFileAge = 17;
FParse::Value( Entry, TEXT("UnusedFileAge="), UnusedFileAge );
int32 MaxFoldersToClean = -1;
FParse::Value( Entry, TEXT("FoldersToClean="), MaxFoldersToClean );
int32 MaxFileChecksPerSec = -1;
FParse::Value( Entry, TEXT("MaxFileChecksPerSec="), MaxFileChecksPerSec );
if( bFlush )
{
IFileManager::Get().DeleteDirectory( *(Path / TEXT("")), false, true );
}
else if( bClean )
{
DeleteOldFiles( *Path );
}
FDerivedDataBackendInterface* InnerFileSystem = NULL;
// Don't create the file system if shared data cache directory is not mounted
if( FCString::Strcmp(NodeName, TEXT("Shared")) != 0 || IFileManager::Get().DirectoryExists(*Path) )
{
InnerFileSystem = CreateFileSystemDerivedDataBackend( *Path, bReadOnly, bTouch, bPurgeTransient, bDeleteUnused, UnusedFileAge, MaxFoldersToClean, MaxFileChecksPerSec);
}
if( InnerFileSystem )
{
DataCache = new FDerivedDataBackendCorruptionWrapper( InnerFileSystem );
UE_LOG( LogDerivedDataCache, Log, TEXT("Using %s data cache path %s: %s"), NodeName, *Path, bReadOnly ? TEXT("ReadOnly") : TEXT("Writable") );
Directories.AddUnique(Path);
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("%s data cache path was not usable, will not use it."), NodeName );
}
}
return DataCache;
}
/**
* Creates Boot data cache interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @param OutFilename filename specified for the cache
* @return Boot data cache backend interface instance or NULL if unsuccessfull
*/
FMemoryDerivedDataBackend* ParseBootCache( const TCHAR* NodeName, const TCHAR* Entry, FString& OutFilename )
{
FMemoryDerivedDataBackend* Cache = NULL;
FString Filename;
int64 MaxCacheSize = -1; // in MB
const int64 MaxSupportedCacheSize = 2048; // 2GB
FParse::Value( Entry, TEXT("MaxCacheSize="), MaxCacheSize);
FParse::Value( Entry, TEXT("Filename="), Filename );
if ( !Filename.Len() )
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendGraph: %s filename not found in *engine.ini, will not use %s cache."), NodeName, NodeName );
}
else
{
// make sure MaxCacheSize does not exceed 2GB
MaxCacheSize = FMath::Min(MaxCacheSize, MaxSupportedCacheSize);
UE_LOG( LogDerivedDataCache, Display, TEXT("Max Cache Size: %d MB"), MaxCacheSize);
Cache = new FMemoryDerivedDataBackend(MaxCacheSize * 1024 * 1024);
if( Cache && Filename.Len() )
{
OutFilename = Filename;
if (MaxCacheSize > 0 && IFileManager::Get().FileSize(*Filename) >= (MaxCacheSize * 1024 * 1024))
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendGraph: %s filename exceeds max size."), NodeName );
return Cache;
}
if (IFileManager::Get().FileSize(*Filename) < 0)
{
UE_LOG( LogDerivedDataCache, Display, TEXT("Starting with empty %s cache"), NodeName );
}
else if( Cache->LoadCache( *Filename ) )
{
UE_LOG( LogDerivedDataCache, Display, TEXT("Loaded %s cache: %s"), NodeName, *Filename );
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Could not load %s cache: %s"), NodeName, *Filename );
}
}
}
return Cache;
}
/**
* Creates Memory data cache interface from ini settings.
*
* @param NodeName Node name.
* @param Entry Node definition.
* @return Memory data cache backend interface instance or NULL if unsuccessfull
*/
FMemoryDerivedDataBackend* ParseMemoryCache( const TCHAR* NodeName, const TCHAR* Entry )
{
FMemoryDerivedDataBackend* Cache = NULL;
FString Filename;
FParse::Value( Entry, TEXT("Filename="), Filename );
Cache = new FMemoryDerivedDataBackend;
if( Cache && Filename.Len() )
{
if( Cache->LoadCache( *Filename ) )
{
UE_LOG( LogDerivedDataCache, Display, TEXT("Loaded %s cache: %s"), NodeName, *Filename );
}
else
{
UE_LOG( LogDerivedDataCache, Warning, TEXT("Could not load %s cache: %s"), NodeName, *Filename );
}
}
return Cache;
}
~FDerivedDataBackendGraph()
{
RootCache = NULL;
DestroyCreatedBackends();
}
FDerivedDataBackendInterface& GetRoot() override
{
check(RootCache);
return *RootCache;
}
virtual void NotifyBootComplete() override
{
check(RootCache);
if (BootCache)
{
if( !FParse::Param( FCommandLine::Get(), TEXT("DDCNOSAVEBOOT") ) && !FParse::Param( FCommandLine::Get(), TEXT("Multiprocess") ) )
{
BootCache->SaveCache(*BootCacheFilename);
}
BootCache->Disable();
}
}
virtual void WaitForQuiescence(bool bShutdown) override
{
double StartTime = FPlatformTime::Seconds();
double LastPrint = StartTime;
while (AsyncCompletionCounter.GetValue())
{
check(AsyncCompletionCounter.GetValue() >= 0);
FPlatformProcess::Sleep(1.0f);
if (FPlatformTime::Seconds() - LastPrint > 5.0)
{
UE_LOG(LogDerivedDataCache, Log, TEXT("Waited %ds for derived data cache to finish..."), int32(FPlatformTime::Seconds() - StartTime));
LastPrint = FPlatformTime::Seconds();
}
}
if (bShutdown)
{
FString MergePaks;
if(WritePakCache && WritePakCache->IsWritable() && FParse::Value(FCommandLine::Get(), TEXT("MergePaks="), MergePaks))
{
TArray<FString> MergePakList;
MergePaks.FString::ParseIntoArray(MergePakList, TEXT("+"));
for(const FString& MergePakName : MergePakList)
{
FPakFileDerivedDataBackend ReadPak(*FPaths::Combine(*FPaths::GetPath(WritePakFilename), *MergePakName), false);
WritePakCache->MergeCache(&ReadPak);
}
}
for (int32 ReadPakIndex = 0; ReadPakIndex < ReadPakCache.Num(); ReadPakIndex++)
{
ReadPakCache[ReadPakIndex]->Close();
}
if (WritePakCache && WritePakCache->IsWritable())
{
WritePakCache->Close();
if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*WritePakFilename))
{
UE_LOG(LogDerivedDataCache, Error, TEXT("Pak file %s was not produced?"), *WritePakFilename);
}
else
{
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*ReadPakFilename))
{
FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*ReadPakFilename, false);
if (!FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*ReadPakFilename))
{
UE_LOG(LogDerivedDataCache, Error, TEXT("Could not delete the pak file %s to overwrite it with a new one."), *ReadPakFilename);
}
}
if (!FPakFileDerivedDataBackend::SortAndCopy(WritePakFilename, ReadPakFilename))
{
UE_LOG(LogDerivedDataCache, Error, TEXT("Couldn't sort pak file (%s)"), *WritePakFilename);
}
else if (!IFileManager::Get().Delete(*WritePakFilename))
{
UE_LOG(LogDerivedDataCache, Error, TEXT("Couldn't delete pak file (%s)"), *WritePakFilename);
}
else
{
UE_LOG(LogDerivedDataCache, Display, TEXT("Sucessfully wrote %s."), *ReadPakFilename);
}
}
}
}
}
virtual void AddToAsyncCompletionCounter(int32 Addend) override
{
AsyncCompletionCounter.Add(Addend);
check(AsyncCompletionCounter.GetValue() >= 0);
}
virtual void GetDirectories(TArray<FString>& OutResults) override
{
OutResults = Directories;
}
static FORCEINLINE FDerivedDataBackendGraph& Get()
{
static FDerivedDataBackendGraph SingletonInstance;
return SingletonInstance;
}
virtual FDerivedDataBackendInterface* MountPakFile(const TCHAR* PakFilename) override
{
// Assumptions: there's at least one read-only pak backend in the hierarchy
// and its parent is a hierarchical backend.
FPakFileDerivedDataBackend* ReadPak = NULL;
if (HierarchicalWrapper && FPlatformFileManager::Get().GetPlatformFile().FileExists(PakFilename))
{
ReadPak = new FPakFileDerivedDataBackend(PakFilename, false);
HierarchicalWrapper->AddInnerBackend(ReadPak);
CreatedBackends.Add(ReadPak);
ReadPakCache.Add(ReadPak);
}
else
{
UE_LOG(LogDerivedDataCache, Warning, TEXT("Failed to add %s read-only pak DDC backend. Make sure it exists and there's at least one hierarchical backend in the cache tree."), PakFilename);
}
return ReadPak;
}
virtual bool UnmountPakFile(const TCHAR* PakFilename) override
{
for (int PakIndex = 0; PakIndex < ReadPakCache.Num(); ++PakIndex)
{
FPakFileDerivedDataBackend* ReadPak = ReadPakCache[PakIndex];
if (ReadPak->GetFilename() == PakFilename)
{
check(HierarchicalWrapper);
// Wait until all async requests are complete.
WaitForQuiescence(false);
HierarchicalWrapper->RemoveInnerBackend(ReadPak);
ReadPakCache.RemoveAt(PakIndex);
CreatedBackends.Remove(ReadPak);
ReadPak->Close();
delete ReadPak;
return true;
}
}
return false;
}
Copying //UE4/Orion-Staging to //UE4/Main (Origin //Orion/Dev-General @ 2870388) #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2870336 on 2016/02/17 by Marc.Audy Continued splitting up Orion Build * Restructure from platform based MakeBuild steps in to a PS4, Server, and Windows Client MakeBuild * Cook server data only once for both Windows and Linux (windows reuses Linux server data) * Split compilation of Win64 Client and Server such that MakeBuild_Server only builds Server and MakeBuild_WindowsClient only builds Client #jira UEB-580 #rb Ben.Marsh #tests Preflight and generated Windows Client and Server work to play game Change 2870026 on 2016/02/17 by Wes.Hunt Don't allow array shrinking when removing the corruption wrapper trailer. #rb none Updating CIS Counter Change 2869725 on 2016/02/17 by Dmitry.Rekman More analytics and QoS stats added for 0.19. #rb none #tests Ran Windows client and Linux server on compatible content. Change 2869705 on 2016/02/16 by Ryan.Gerleve Fix replicated properties and call RepNotifies of startup actors when scrubbing in replays. This is the engine support for fixing OR-6817, towers not respawning when rewinding replays. #rb john.pollard #tests golden path, replays, ps4 nomcp Change 2869644 on 2016/02/16 by Jason.Bestimt #ORION_DEV - Merge MAIN (0.18) at CL# 2869635 #Tests:none #RB:none Change 2869586 on 2016/02/16 by Marcus.Wassmer Fix texturestreaming RHI flushes. #rb none #test goldenpath #codereview Gil.Gribb Change 2869279 on 2016/02/16 by Lukasz.Furman fixed minion hit reaction directions #orion OR-13953 #rb Mieszko.Zielinski #tests PIE: hit minions with various abilities from different angles, checked velocity of death particles when killed by abilities and towers #codereview Dan.Youhon Change 2869277 on 2016/02/16 by Wes.Hunt During cook, when a package is not ready to save, actually early out of the saving code. Saves somewhere in the 130s to 200s range for cooks. #rb daniel.lamb #tests local windows cooks, preflight PS4 cooks Change 2869132 on 2016/02/16 by Mieszko.Zielinski Added a function to AISenseConfig allowing native-code MaxAge configuration #UE4 #rb Lukasz.Furman #test none required Change 2868981 on 2016/02/16 by Wes.Hunt remove -LogCookStats cmdline check, always log cook stats. -SendCookAnalytics flag is still used. This was requested by NickP. #rb none #tests local windows cooks Change 2868975 on 2016/02/16 by Wes.Hunt Don't submit DDC usage stats for zero-sized events. #rb none #tests local windows cook Change 2868956 on 2016/02/16 by Jason.Bestimt #ORION_DEV - Merge MAIN (0.18) at CL# 2868926 #RB:none #Tests:none Change 2868889 on 2016/02/16 by Max.Chen Sequencer: Only allow transport control binding when editing level editor sequencers. #rb none #tests none Change 2868663 on 2016/02/16 by David.Ratti downgrade warning to display #rb none #tests compile Change 2868624 on 2016/02/16 by Marcus.Wassmer Re-Enable Defrag validation for devgeneral #rb none #test none Change 2868493 on 2016/02/16 by Benn.Gallagher Added a few more stats to morph target updates to try and narrow down hitches #rb Bruce.Nesbit #tests pie, -game Win64 Change 2868445 on 2016/02/16 by Dmitry.Rekman Linux: report crashes due to stack overflow (OR-14519). - Reserve memory for alternative stack for signal handlers. Adds about 128KB memory per thread. - Force process spawning to use vfork() when no pipes are needed. - Ignore all signals except explicitly handled. - Prevent signals from being raised while another one is handled. - Added "debug threadrecurse" and "debug threadstackoverflow" to test that. [CL 2873763 by Andrew Grant in Main branch]
2016-02-19 12:03:17 -05:00
virtual void GatherUsageStats(TMap<FString, FDerivedDataCacheUsageStats>& UsageStats) override
{
if (RootCache)
{
RootCache->GatherUsageStats(UsageStats, TEXT(" 0"));
}
}
private:
/** Delete the old files in a directory **/
void DeleteOldFiles(const TCHAR* Directory)
{
float MinimumDaysToKeepFile = 7;
GConfig->GetFloat( *GraphName, TEXT("MinimumDaysToKeepFile"), MinimumDaysToKeepFile, GEngineIni );
check(MinimumDaysToKeepFile > 0.0f); // sanity
//@todo
}
/** Delete all created backends in the reversed order they were created. */
void DestroyCreatedBackends()
{
for (int32 BackendIndex = CreatedBackends.Num() - 1; BackendIndex >= 0; --BackendIndex)
{
delete CreatedBackends[BackendIndex];
}
CreatedBackends.Empty();
}
/** MountPak console command handler. */
void UnmountPakCommandHandler(const TArray<FString>& Args)
{
if (Args.Num() < 1)
{
UE_LOG(LogDerivedDataCache, Log, TEXT("Usage: DDC.MountPak PakFilename"));
return;
}
UnmountPakFile(*Args[0]);
}
/** UnmountPak console command handler. */
void MountPakCommandHandler(const TArray<FString>& Args)
{
if (Args.Num() < 1)
{
UE_LOG(LogDerivedDataCache, Log, TEXT("Usage: DDC.UnmountPak PakFilename"));
return;
}
MountPakFile(*Args[0]);
}
FThreadSafeCounter AsyncCompletionCounter;
FString GraphName;
FString BootCacheFilename;
FString ReadPakFilename;
FString WritePakFilename;
/** Root of the graph */
FDerivedDataBackendInterface* RootCache;
/** References to all created backed interfaces */
TArray< FDerivedDataBackendInterface* > CreatedBackends;
/** Instances of backend interfaces which exist in only one copy */
FMemoryDerivedDataBackend* BootCache;
FPakFileDerivedDataBackend* WritePakCache;
FDerivedDataBackendInterface* AsyncPutWrapper;
FDerivedDataBackendInterface* KeyLengthWrapper;
FHierarchicalDerivedDataBackend* HierarchicalWrapper;
/** Support for multiple read only pak files. */
TArray<FPakFileDerivedDataBackend*> ReadPakCache;
/** List of directories used by the DDC */
TArray<FString> Directories;
/** MountPak console command */
FAutoConsoleCommand MountPakCommand;
/** UnmountPak console command */
FAutoConsoleCommand UnountPakCommand;
};
FDerivedDataBackend& FDerivedDataBackend::Get()
{
return FDerivedDataBackendGraph::Get();
}
#undef LOCTEXT_NAMESPACE