2019-12-26 15:32:37 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2015-08-10 08:14:45 -04:00
2021-01-21 01:57:01 -04:00
# include "DerivedDataCache.h"
# include "DerivedDataCacheInterface.h"
2015-08-10 08:14:45 -04:00
2021-01-21 16:22:06 -04:00
# include "Algo/AllOf.h"
2016-11-23 15:48:37 -05:00
# include "Async/AsyncWork.h"
2017-11-06 18:22:01 -05:00
# include "Async/TaskGraphInterfaces.h"
2022-01-25 09:50:39 -05:00
# include "Containers/Map.h"
2021-05-21 12:24:43 -04:00
# include "DDCCleanup.h"
2015-08-10 08:14:45 -04:00
# include "DerivedDataBackendInterface.h"
2021-08-05 13:12:08 -04:00
# include "DerivedDataCache.h"
2021-11-10 13:08:00 -05:00
# include "DerivedDataCacheMaintainer.h"
2021-03-10 12:23:29 -04:00
# include "DerivedDataCachePrivate.h"
2021-06-22 00:27:54 -04:00
# include "DerivedDataCacheUsageStats.h"
2015-08-10 08:14:45 -04:00
# include "DerivedDataPluginInterface.h"
2022-03-23 16:56:39 -04:00
# include "DerivedDataRequest.h"
2022-01-13 17:30:44 -05:00
# include "DerivedDataRequestOwner.h"
2022-03-23 16:56:39 -04:00
# include "Experimental/Async/LazyEvent.h"
2021-11-10 13:08:00 -05:00
# include "Features/IModularFeatures.h"
2021-05-21 12:24:43 -04:00
# include "HAL/ThreadSafeCounter.h"
2021-01-22 12:20:09 -04:00
# include "Misc/CommandLine.h"
2021-11-10 13:08:00 -05:00
# include "Misc/CoreMisc.h"
2021-05-21 12:24:43 -04:00
# include "Misc/ScopeLock.h"
# include "ProfilingDebugging/CookStats.h"
2022-06-20 23:40:01 -04:00
# include "Serialization/CompactBinary.h"
# include "Serialization/CompactBinarySerialization.h"
# include "Serialization/CompactBinaryWriter.h"
2021-05-21 12:24:43 -04:00
# include "Stats/Stats.h"
# include "Stats/StatsMisc.h"
2021-11-10 13:08:00 -05:00
# include "ZenServerInterface.h"
2020-09-24 00:43:27 -04:00
# include <atomic>
2015-08-10 08:14:45 -04:00
DEFINE_STAT ( STAT_DDC_NumGets ) ;
DEFINE_STAT ( STAT_DDC_NumPuts ) ;
DEFINE_STAT ( STAT_DDC_NumBuilds ) ;
DEFINE_STAT ( STAT_DDC_NumExist ) ;
DEFINE_STAT ( STAT_DDC_SyncGetTime ) ;
DEFINE_STAT ( STAT_DDC_ASyncWaitTime ) ;
DEFINE_STAT ( STAT_DDC_PutTime ) ;
DEFINE_STAT ( STAT_DDC_SyncBuildTime ) ;
DEFINE_STAT ( STAT_DDC_ExistTime ) ;
2019-06-03 15:32:00 -04:00
//#define DDC_SCOPE_CYCLE_COUNTER(x) QUICK_SCOPE_CYCLE_COUNTER(STAT_ ## x)
2019-10-03 16:26:48 -04:00
# define DDC_SCOPE_CYCLE_COUNTER(x) TRACE_CPUPROFILER_EVENT_SCOPE(x);
2019-06-03 15:32:00 -04:00
2016-02-19 12:03:17 -05:00
# if ENABLE_COOK_STATS
# include "DerivedDataCacheUsageStats.h"
2022-01-13 11:09:29 -05:00
namespace UE : : DerivedData : : CookStats
2016-02-19 12:03:17 -05:00
{
2019-10-03 16:26:48 -04:00
// Use to prevent potential divide by zero issues
inline double SafeDivide ( const int64 Numerator , const int64 Denominator )
{
return Denominator ! = 0 ? ( double ) Numerator / ( double ) Denominator : 0.0 ;
}
2020-02-04 11:54:28 -05:00
// AddCookStats cannot be a lambda because of false positives in static analysis.
// See https://developercommunity.visualstudio.com/content/problem/576913/c6244-regression-in-new-lambda-processorpermissive.html
static void AddCookStats ( FCookStatsManager : : AddStatFuncRef AddStat )
2016-02-19 12:03:17 -05:00
{
2022-01-03 11:24:10 -05:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS ;
TSharedRef < FDerivedDataCacheStatsNode > RootNode = GetDerivedDataCacheRef ( ) . GatherUsageStats ( ) ;
PRAGMA_ENABLE_DEPRECATION_WARNINGS ;
2016-02-19 12:03:17 -05:00
{
const FString StatName ( TEXT ( " DDC.Usage " ) ) ;
2022-01-03 11:24:10 -05:00
for ( const auto & UsageStatPair : RootNode - > ToLegacyUsageMap ( ) )
2016-02-19 12:03:17 -05:00
{
2016-05-16 16:20:52 -04:00
UsageStatPair . Value . LogStats ( AddStat , StatName , UsageStatPair . Key ) ;
2016-02-19 12:03:17 -05:00
}
}
2022-01-03 11:24:10 -05:00
TArray < TSharedRef < const FDerivedDataCacheStatsNode > > Nodes ;
RootNode - > ForEachDescendant ( [ & Nodes ] ( TSharedRef < const FDerivedDataCacheStatsNode > Node )
{
if ( Node - > Children . IsEmpty ( ) )
{
Nodes . Add ( Node ) ;
}
} ) ;
2016-02-19 12:03:17 -05:00
// Now lets add some summary data to that applies some crazy knowledge of how we set up our DDC. The goal
// is to print out the global hit rate, and the hit rate of the local and shared DDC.
// This is done by adding up the total get/miss calls the root node receives.
// Then we find the FileSystem nodes that correspond to the local and shared cache using some hacky logic to detect a "network drive".
// If the DDC graph ever contains more than one local or remote filesystem, this will only find one of them.
{
2022-01-03 11:24:10 -05:00
const TSharedRef < const FDerivedDataCacheStatsNode > * LocalNode = Nodes . FindByPredicate ( [ ] ( TSharedRef < const FDerivedDataCacheStatsNode > Node ) { return Node - > GetCacheType ( ) = = TEXT ( " File System " ) & & Node - > IsLocal ( ) ; } ) ;
const TSharedRef < const FDerivedDataCacheStatsNode > * SharedNode = Nodes . FindByPredicate ( [ ] ( TSharedRef < const FDerivedDataCacheStatsNode > Node ) { return Node - > GetCacheType ( ) = = TEXT ( " File System " ) & & ! Node - > IsLocal ( ) ; } ) ;
const TSharedRef < const FDerivedDataCacheStatsNode > * CloudNode = Nodes . FindByPredicate ( [ ] ( TSharedRef < const FDerivedDataCacheStatsNode > Node ) { return Node - > GetCacheType ( ) = = TEXT ( " Horde Storage " ) ; } ) ;
const TSharedRef < const FDerivedDataCacheStatsNode > * ZenLocalNode = Nodes . FindByPredicate ( [ ] ( TSharedRef < const FDerivedDataCacheStatsNode > Node ) { return Node - > GetCacheType ( ) = = TEXT ( " Zen " ) & & Node - > IsLocal ( ) ; } ) ;
const TSharedRef < const FDerivedDataCacheStatsNode > * ZenRemoteNode = Nodes . FindByPredicate ( [ ] ( TSharedRef < const FDerivedDataCacheStatsNode > Node ) { return ( Node - > GetCacheType ( ) = = TEXT ( " Zen " ) | | Node - > GetCacheType ( ) = = TEXT ( " Horde " ) ) & & ! Node - > IsLocal ( ) ; } ) ;
2021-01-22 12:20:09 -04:00
2022-05-31 05:11:05 -04:00
const FDerivedDataCacheUsageStats & RootStats = RootNode - > UsageStats . CreateConstIterator ( ) . Value ( ) ;
2022-01-03 11:24:10 -05:00
const int64 TotalGetHits = RootStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
const int64 TotalGetMisses = RootStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Miss , FCookStats : : CallStats : : EStatType : : Counter ) ;
const int64 TotalGets = TotalGetHits + TotalGetMisses ;
int64 LocalHits = 0 ;
2022-05-31 05:11:05 -04:00
FDerivedDataCacheSpeedStats LocalSpeedStats ;
2022-01-03 11:24:10 -05:00
if ( LocalNode )
2016-02-19 12:03:17 -05:00
{
2022-05-31 05:11:05 -04:00
const FDerivedDataCacheUsageStats & UsageStats = ( * LocalNode ) - > UsageStats . CreateConstIterator ( ) . Value ( ) ;
LocalHits + = UsageStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
LocalSpeedStats = ( * LocalNode ) - > SpeedStats ;
2016-02-19 12:03:17 -05:00
}
2022-01-03 11:24:10 -05:00
if ( ZenLocalNode )
{
2022-05-31 05:11:05 -04:00
const FDerivedDataCacheUsageStats & UsageStats = ( * ZenLocalNode ) - > UsageStats . CreateConstIterator ( ) . Value ( ) ;
LocalHits + = UsageStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
LocalSpeedStats = ( * ZenLocalNode ) - > SpeedStats ;
2022-01-03 11:24:10 -05:00
}
int64 SharedHits = 0 ;
2022-05-31 05:11:05 -04:00
FDerivedDataCacheSpeedStats SharedSpeedStats ;
2022-01-03 11:24:10 -05:00
if ( SharedNode )
{
// The shared DDC is only queried if the local one misses (or there isn't one). So it's hit rate is technically
2022-05-31 05:11:05 -04:00
const FDerivedDataCacheUsageStats & UsageStats = ( * SharedNode ) - > UsageStats . CreateConstIterator ( ) . Value ( ) ;
SharedHits + = UsageStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
SharedSpeedStats = ( * SharedNode ) - > SpeedStats ;
2022-01-03 11:24:10 -05:00
}
if ( ZenRemoteNode )
{
2022-05-31 05:11:05 -04:00
const FDerivedDataCacheUsageStats & UsageStats = ( * ZenRemoteNode ) - > UsageStats . CreateConstIterator ( ) . Value ( ) ;
SharedHits + = UsageStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
SharedSpeedStats = ( * ZenRemoteNode ) - > SpeedStats ;
2022-01-03 11:24:10 -05:00
}
int64 CloudHits = 0 ;
2022-05-31 05:11:05 -04:00
FDerivedDataCacheSpeedStats CloudSpeedStats ;
2022-01-03 11:24:10 -05:00
if ( CloudNode )
{
2022-05-31 05:11:05 -04:00
const FDerivedDataCacheUsageStats & UsageStats = ( * CloudNode ) - > UsageStats . CreateConstIterator ( ) . Value ( ) ;
CloudHits + = UsageStats . GetStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
CloudSpeedStats = ( * CloudNode ) - > SpeedStats ;
2022-01-03 11:24:10 -05:00
}
const int64 TotalPutHits = RootStats . PutStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter ) ;
const int64 TotalPutMisses = RootStats . PutStats . GetAccumulatedValueAnyThread ( FCookStats : : CallStats : : EHitOrMiss : : Miss , FCookStats : : CallStats : : EStatType : : Counter ) ;
const int64 TotalPuts = TotalPutHits + TotalPutMisses ;
AddStat ( TEXT ( " DDC.Summary " ) , FCookStatsManager : : CreateKeyValueArray (
TEXT ( " BackEnd " ) , FDerivedDataBackend : : Get ( ) . GetGraphName ( ) ,
TEXT ( " HasLocalCache " ) , LocalNode | | ZenLocalNode ,
TEXT ( " HasSharedCache " ) , SharedNode | | ZenRemoteNode ,
TEXT ( " HasCloudCache " ) , ! ! CloudNode ,
TEXT ( " HasZenCache " ) , ZenLocalNode | | ZenRemoteNode ,
TEXT ( " TotalGetHits " ) , TotalGetHits ,
TEXT ( " TotalGets " ) , TotalGets ,
TEXT ( " TotalGetHitPct " ) , SafeDivide ( TotalGetHits , TotalGets ) ,
TEXT ( " LocalGetHitPct " ) , SafeDivide ( LocalHits , TotalGets ) ,
TEXT ( " SharedGetHitPct " ) , SafeDivide ( SharedHits , TotalGets ) ,
TEXT ( " CloudGetHitPct " ) , SafeDivide ( CloudHits , TotalGets ) ,
TEXT ( " OtherGetHitPct " ) , SafeDivide ( ( TotalGetHits - LocalHits - SharedHits - CloudHits ) , TotalGets ) ,
TEXT ( " GetMissPct " ) , SafeDivide ( TotalGetMisses , TotalGets ) ,
TEXT ( " TotalPutHits " ) , TotalPutHits ,
TEXT ( " TotalPuts " ) , TotalPuts ,
TEXT ( " TotalPutHitPct " ) , SafeDivide ( TotalPutHits , TotalPuts ) ,
2022-05-31 05:11:05 -04:00
TEXT ( " PutMissPct " ) , SafeDivide ( TotalPutMisses , TotalPuts ) ,
TEXT ( " LocalLatency " ) , LocalSpeedStats . LatencyMS ,
TEXT ( " LocalReadSpeed " ) , LocalSpeedStats . ReadSpeedMBs ,
TEXT ( " LocalWriteSpeed " ) , LocalSpeedStats . WriteSpeedMBs ,
TEXT ( " SharedLatency " ) , SharedSpeedStats . LatencyMS ,
TEXT ( " SharedReadSpeed " ) , SharedSpeedStats . ReadSpeedMBs ,
TEXT ( " SharedWriteSpeed " ) , SharedSpeedStats . WriteSpeedMBs ,
TEXT ( " CloudLatency " ) , CloudSpeedStats . LatencyMS ,
TEXT ( " CloudReadSpeed " ) , CloudSpeedStats . ReadSpeedMBs ,
TEXT ( " CloudWriteSpeed " ) , CloudSpeedStats . WriteSpeedMBs
) ) ;
2016-02-19 12:03:17 -05:00
}
2020-02-04 11:54:28 -05:00
}
FCookStatsManager : : FAutoRegisterCallback RegisterCookStats ( AddCookStats ) ;
2016-02-19 12:03:17 -05:00
}
# endif
2021-07-29 13:07:00 -04:00
void GatherDerivedDataCacheResourceStats ( TArray < FDerivedDataCacheResourceStat > & DDCResourceStats ) ;
void GatherDerivedDataCacheSummaryStats ( FDerivedDataCacheSummaryStats & DDCSummaryStats ) ;
2017-07-14 06:36:47 -04:00
/** Whether we want to verify the DDC (pass in -VerifyDDC on the command line)*/
bool GVerifyDDC = false ;
2021-10-07 09:11:32 -04:00
namespace UE : : DerivedData
{
2022-01-26 17:07:56 -05:00
FCachePutResponse FCachePutRequest : : MakeResponse ( const EStatus Status ) const
{
return { Name , Record . GetKey ( ) , UserData , Status } ;
}
FCacheGetResponse FCacheGetRequest : : MakeResponse ( const EStatus Status ) const
{
return { Name , FCacheRecordBuilder ( Key ) . Build ( ) , UserData , Status } ;
}
FCachePutValueResponse FCachePutValueRequest : : MakeResponse ( const EStatus Status ) const
{
return { Name , Key , UserData , Status } ;
}
FCacheGetValueResponse FCacheGetValueRequest : : MakeResponse ( const EStatus Status ) const
{
return { Name , Key , { } , UserData , Status } ;
}
FCacheGetChunkResponse FCacheGetChunkRequest : : MakeResponse ( const EStatus Status ) const
{
return { Name , Key , Id , RawOffset , 0 , { } , { } , UserData , Status } ;
}
2022-06-20 23:40:01 -04:00
FCbWriter & operator < < ( FCbWriter & Writer , const FCacheGetRequest & Request )
{
Writer . BeginObject ( ) ;
if ( ! Request . Name . IsEmpty ( ) )
{
Writer < < ANSITEXTVIEW ( " Name " ) < < Request . Name ;
}
Writer < < ANSITEXTVIEW ( " Key " ) < < Request . Key ;
if ( ! Request . Policy . IsDefault ( ) )
{
Writer < < ANSITEXTVIEW ( " Policy " ) < < Request . Policy ;
}
if ( Request . UserData ! = 0 )
{
Writer < < ANSITEXTVIEW ( " UserData " ) < < Request . UserData ;
}
Writer . EndObject ( ) ;
return Writer ;
}
bool LoadFromCompactBinary ( FCbFieldView Field , FCacheGetRequest & Request )
{
bool bOk = Field . IsObject ( ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Name " ) ] , Request . Name ) ;
bOk & = LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Key " ) ] , Request . Key ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Policy " ) ] , Request . Policy ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " UserData " ) ] , Request . UserData ) ;
return bOk ;
}
FCbWriter & operator < < ( FCbWriter & Writer , const FCacheGetValueRequest & Request )
{
Writer . BeginObject ( ) ;
if ( ! Request . Name . IsEmpty ( ) )
{
Writer < < ANSITEXTVIEW ( " Name " ) < < MakeStringView ( Request . Name ) ;
}
Writer < < ANSITEXTVIEW ( " Key " ) < < Request . Key ;
if ( Request . Policy ! = ECachePolicy : : Default )
{
Writer < < ANSITEXTVIEW ( " Policy " ) < < Request . Policy ;
}
if ( Request . UserData ! = 0 )
{
Writer < < ANSITEXTVIEW ( " UserData " ) < < Request . UserData ;
}
Writer . EndObject ( ) ;
return Writer ;
}
bool LoadFromCompactBinary ( FCbFieldView Field , FCacheGetValueRequest & Request )
{
bool bOk = Field . IsObject ( ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Name " ) ] , Request . Name ) ;
bOk & = LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Key " ) ] , Request . Key ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Policy " ) ] , Request . Policy ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " UserData " ) ] , Request . UserData ) ;
return bOk ;
}
FCbWriter & operator < < ( FCbWriter & Writer , const FCacheGetChunkRequest & Request )
{
Writer . BeginObject ( ) ;
if ( ! Request . Name . IsEmpty ( ) )
{
Writer < < ANSITEXTVIEW ( " Name " ) < < MakeStringView ( Request . Name ) ;
}
Writer < < ANSITEXTVIEW ( " Key " ) < < Request . Key ;
if ( Request . Id . IsValid ( ) )
{
Writer < < ANSITEXTVIEW ( " Id " ) < < Request . Id ;
}
if ( Request . RawOffset ! = 0 )
{
Writer < < ANSITEXTVIEW ( " RawOffset " ) < < Request . RawOffset ;
}
if ( Request . RawSize ! = MAX_uint64 )
{
Writer < < ANSITEXTVIEW ( " RawSize " ) < < Request . RawSize ;
}
if ( ! Request . RawHash . IsZero ( ) )
{
Writer < < ANSITEXTVIEW ( " RawHash " ) < < Request . RawHash ;
}
if ( Request . Policy ! = ECachePolicy : : Default )
{
Writer < < ANSITEXTVIEW ( " Policy " ) < < Request . Policy ;
}
if ( Request . UserData )
{
Writer < < ANSITEXTVIEW ( " UserData " ) < < Request . UserData ;
}
Writer . EndObject ( ) ;
return Writer ;
}
bool LoadFromCompactBinary ( FCbFieldView Field , FCacheGetChunkRequest & OutRequest )
{
bool bOk = Field . IsObject ( ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Name " ) ] , OutRequest . Name ) ;
bOk & = LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Key " ) ] , OutRequest . Key ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Id " ) ] , OutRequest . Id ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " RawOffset " ) ] , OutRequest . RawOffset , 0 ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " RawSize " ) ] , OutRequest . RawSize , MAX_uint64 ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " RawHash " ) ] , OutRequest . RawHash ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " Policy " ) ] , OutRequest . Policy ) ;
LoadFromCompactBinary ( Field [ ANSITEXTVIEW ( " UserData " ) ] , OutRequest . UserData ) ;
return bOk ;
}
2021-10-07 09:11:32 -04:00
} // UE::DerivedData
2021-05-21 12:24:43 -04:00
namespace UE : : DerivedData : : Private
2021-04-28 23:25:28 -04:00
{
2022-01-18 04:45:24 -05:00
FQueuedThreadPool * GCacheThreadPool ;
2022-03-23 16:56:39 -04:00
class FCacheThreadPoolTaskRequest final : public FRequestBase , private IQueuedWork
{
public :
2022-09-21 14:25:32 -04:00
inline FCacheThreadPoolTaskRequest ( IRequestOwner & InOwner , TUniqueFunction < void ( ) > & & InTaskBody )
2022-03-23 16:56:39 -04:00
: Owner ( InOwner )
, TaskBody ( MoveTemp ( InTaskBody ) )
{
Owner . Begin ( this ) ;
DoneEvent . Reset ( ) ;
2022-09-21 14:25:32 -04:00
GCacheThreadPool - > AddQueuedWork ( this , ConvertToQueuedWorkPriority ( Owner . GetPriority ( ) ) ) ;
2022-03-23 16:56:39 -04:00
}
2022-09-21 14:25:32 -04:00
private :
2022-03-23 16:56:39 -04:00
inline void Execute ( )
{
FScopeCycleCounter Scope ( GetStatId ( ) , /*bAlways*/ true ) ;
Owner . End ( this , [ this ]
{
TaskBody ( ) ;
DoneEvent . Trigger ( ) ;
} ) ;
// DO NOT ACCESS ANY MEMBERS PAST THIS POINT!
}
// IRequest Interface
inline void SetPriority ( EPriority Priority ) final
{
if ( GCacheThreadPool - > RetractQueuedWork ( this ) )
{
2022-09-21 14:25:32 -04:00
GCacheThreadPool - > AddQueuedWork ( this , ConvertToQueuedWorkPriority ( Priority ) ) ;
2022-03-23 16:56:39 -04:00
}
}
inline void Cancel ( ) final
{
if ( ! DoneEvent . Wait ( 0 ) )
{
if ( GCacheThreadPool - > RetractQueuedWork ( this ) )
{
Abandon ( ) ;
}
else
{
FScopeCycleCounter Scope ( GetStatId ( ) ) ;
DoneEvent . Wait ( ) ;
}
}
}
inline void Wait ( ) final
{
if ( ! DoneEvent . Wait ( 0 ) )
{
if ( GCacheThreadPool - > RetractQueuedWork ( this ) )
{
DoThreadedWork ( ) ;
}
else
{
FScopeCycleCounter Scope ( GetStatId ( ) ) ;
DoneEvent . Wait ( ) ;
}
}
}
// IQueuedWork Interface
inline void DoThreadedWork ( ) final { Execute ( ) ; }
inline void Abandon ( ) final { Execute ( ) ; }
inline TStatId GetStatId ( ) const
{
RETURN_QUICK_DECLARE_CYCLE_STAT ( FCacheThreadPoolTaskRequest , STATGROUP_ThreadPoolAsyncTasks ) ;
}
private :
IRequestOwner & Owner ;
TUniqueFunction < void ( ) > TaskBody ;
FLazyEvent DoneEvent { EEventMode : : ManualReset } ;
} ;
void LaunchTaskInCacheThreadPool ( IRequestOwner & Owner , TUniqueFunction < void ( ) > & & TaskBody )
{
if ( GCacheThreadPool )
{
2022-09-21 14:25:32 -04:00
new FCacheThreadPoolTaskRequest ( Owner , MoveTemp ( TaskBody ) ) ;
2022-03-23 16:56:39 -04:00
}
else
{
TaskBody ( ) ;
}
}
2016-02-19 12:03:17 -05:00
/**
2015-08-10 08:14:45 -04:00
* Implementation of the derived data cache
* This API is fully threadsafe
**/
2021-11-10 13:08:00 -05:00
class FDerivedDataCache final
: public FDerivedDataCacheInterface
, public ICache
, public ICacheStoreMaintainer
, public IDDCCleanup
2015-08-10 08:14:45 -04:00
{
/**
* Async worker that checks the cache backend and if that fails, calls the deriver to build the data and then puts the results to the cache
**/
friend class FBuildAsyncWorker ;
class FBuildAsyncWorker : public FNonAbandonableTask
{
public :
2020-09-24 00:43:27 -04:00
enum EWorkerState : uint32
{
WorkerStateNone = 0 ,
WorkerStateRunning = 1 < < 0 ,
WorkerStateFinished = 1 < < 1 ,
WorkerStateDestroyed = 1 < < 2 ,
} ;
2015-08-10 08:14:45 -04:00
/**
* Constructor for async task
* @param InDataDeriver plugin to produce cache key and in the event of a miss, return the data.
* @param InCacheKey Complete cache key for this data.
**/
2022-02-23 13:47:00 -05:00
FBuildAsyncWorker ( FDerivedDataBackend * InBackend , FDerivedDataPluginInterface * InDataDeriver , const TCHAR * InCacheKey , FStringView InDebugContext , bool bInSynchronousForStats )
2015-08-10 08:14:45 -04:00
: bSuccess ( false )
, bSynchronousForStats ( bInSynchronousForStats )
2016-05-16 16:20:52 -04:00
, bDataWasBuilt ( false )
2022-02-23 13:47:00 -05:00
, Backend ( InBackend )
2015-08-10 08:14:45 -04:00
, DataDeriver ( InDataDeriver )
, CacheKey ( InCacheKey )
2022-01-13 17:30:44 -05:00
, DebugContext ( InDebugContext )
2015-08-10 08:14:45 -04:00
{
}
2020-09-24 00:43:27 -04:00
virtual ~ FBuildAsyncWorker ( )
{
// Record that the task is destroyed and check that it was not running or destroyed previously.
{
const uint32 PreviousState = WorkerState . fetch_or ( WorkerStateDestroyed , std : : memory_order_relaxed ) ;
checkf ( ! ( PreviousState & WorkerStateRunning ) , TEXT ( " Destroying DDC worker that is still running! Key: %s " ) , * CacheKey ) ;
checkf ( ! ( PreviousState & WorkerStateDestroyed ) , TEXT ( " Destroying DDC worker that has been destroyed previously! Key: %s " ) , * CacheKey ) ;
}
}
2015-08-10 08:14:45 -04:00
/** Async worker that checks the cache backend and if that fails, calls the deriver to build the data and then puts the results to the cache **/
void DoWork ( )
{
2020-09-24 00:43:27 -04:00
// Record that the task is running and check that it was not running, finished, or destroyed previously.
{
const uint32 PreviousState = WorkerState . fetch_or ( WorkerStateRunning , std : : memory_order_relaxed ) ;
checkf ( ! ( PreviousState & WorkerStateRunning ) , TEXT ( " Starting DDC worker that is already running! Key: %s " ) , * CacheKey ) ;
checkf ( ! ( PreviousState & WorkerStateFinished ) , TEXT ( " Starting DDC worker that is already finished! Key: %s " ) , * CacheKey ) ;
checkf ( ! ( PreviousState & WorkerStateDestroyed ) , TEXT ( " Starting DDC worker that has been destroyed! Key: %s " ) , * CacheKey ) ;
}
2019-10-03 16:26:48 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( DDC_DoWork ) ;
2019-06-03 15:32:00 -04:00
2022-02-11 12:56:47 -05:00
const int64 NumBeforeDDC = Data . Num ( ) ;
2015-08-10 08:14:45 -04:00
bool bGetResult ;
{
2019-10-03 16:26:48 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( DDC_Get ) ;
2019-06-03 15:32:00 -04:00
2015-08-10 08:14:45 -04:00
INC_DWORD_STAT ( STAT_DDC_NumGets ) ;
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
2022-01-13 17:30:44 -05:00
FLegacyCacheGetRequest LegacyRequest ;
LegacyRequest . Name = DebugContext ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-01-13 17:30:44 -05:00
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyGet ( { LegacyRequest } , BlockingOwner ,
2022-01-13 17:30:44 -05:00
[ this , & bGetResult ] ( FLegacyCacheGetResponse & & Response )
{
2022-02-10 14:57:42 -05:00
const uint64 RawSize = Response . Value . GetRawSize ( ) ;
2022-09-23 13:12:14 -04:00
bGetResult = Response . Status = = EStatus : : Ok & & RawSize > 0 & & RawSize < MAX_int64 ;
2022-01-13 17:30:44 -05:00
if ( bGetResult )
{
2022-02-10 14:57:42 -05:00
const FCompositeBuffer & RawData = Response . Value . GetRawData ( ) ;
2022-02-11 12:56:47 -05:00
Data . Reset ( int64 ( RawSize ) ) ;
2022-02-10 14:57:42 -05:00
for ( const FSharedBuffer & Segment : RawData . GetSegments ( ) )
{
2022-02-11 12:56:47 -05:00
Data . Append ( static_cast < const uint8 * > ( Segment . GetData ( ) ) , int64 ( Segment . GetSize ( ) ) ) ;
2022-02-10 14:57:42 -05:00
}
2022-01-13 17:30:44 -05:00
}
} ) ;
BlockingOwner . Wait ( ) ;
2015-08-10 08:14:45 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_SyncGetTime , bSynchronousForStats ? ( float ) ThisTime : 0.0f ) ;
}
if ( bGetResult )
{
2017-07-14 06:36:47 -04:00
if ( GVerifyDDC & & DataDeriver & & DataDeriver - > IsDeterministic ( ) )
{
TArray < uint8 > CmpData ;
DataDeriver - > Build ( CmpData ) ;
2022-02-11 12:56:47 -05:00
const int64 NumInDDC = Data . Num ( ) - NumBeforeDDC ;
const int64 NumGenerated = CmpData . Num ( ) ;
2017-07-14 06:36:47 -04:00
bool bMatchesInSize = NumGenerated = = NumInDDC ;
bool bDifferentMemory = true ;
2018-02-22 11:25:06 -05:00
int32 DifferentOffset = 0 ;
2017-07-14 06:36:47 -04:00
if ( bMatchesInSize )
{
2018-02-22 11:25:06 -05:00
bDifferentMemory = false ;
for ( int32 i = 0 ; i < NumGenerated ; i + + )
{
if ( CmpData [ i ] ! = Data [ i ] )
{
bDifferentMemory = true ;
DifferentOffset = i ;
break ;
}
}
2017-07-14 06:36:47 -04:00
}
if ( ! bMatchesInSize | | bDifferentMemory )
{
2018-02-22 11:25:06 -05:00
FString ErrMsg = FString : : Printf ( TEXT ( " There is a mismatch between the DDC data and the generated data for plugin (%s) for asset (%s). BytesInDDC:%d, BytesGenerated:%d, bDifferentMemory:%d, offset:%d " ) , DataDeriver - > GetPluginName ( ) , * DataDeriver - > GetDebugContextString ( ) , NumInDDC , NumGenerated , bDifferentMemory , DifferentOffset ) ;
2018-04-26 14:11:04 -04:00
ensureMsgf ( false , TEXT ( " %s " ) , * ErrMsg ) ;
2017-07-14 06:36:47 -04:00
UE_LOG ( LogDerivedDataCache , Error , TEXT ( " %s " ) , * ErrMsg ) ;
}
}
2015-08-10 08:14:45 -04:00
check ( Data . Num ( ) ) ;
bSuccess = true ;
delete DataDeriver ;
DataDeriver = NULL ;
}
else if ( DataDeriver )
{
{
2019-10-03 16:26:48 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( DDC_Build ) ;
2019-06-03 15:32:00 -04:00
2015-08-10 08:14:45 -04:00
INC_DWORD_STAT ( STAT_DDC_NumBuilds ) ;
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
2022-02-11 12:56:47 -05:00
TArray < uint8 > Data32 ;
bSuccess = DataDeriver - > Build ( Data32 ) ;
Data = TArray64 < uint8 > ( MoveTemp ( Data32 ) ) ;
2016-05-16 16:20:52 -04:00
bDataWasBuilt = true ;
2015-08-10 08:14:45 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_SyncBuildTime , bSynchronousForStats ? ( float ) ThisTime : 0.0f ) ;
}
delete DataDeriver ;
DataDeriver = NULL ;
if ( bSuccess )
{
check ( Data . Num ( ) ) ;
2019-06-03 15:32:00 -04:00
2019-10-03 16:26:48 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( DDC_Put ) ;
2019-06-03 15:32:00 -04:00
2015-08-10 08:14:45 -04:00
INC_DWORD_STAT ( STAT_DDC_NumPuts ) ;
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
2022-01-13 17:30:44 -05:00
FLegacyCachePutRequest LegacyRequest ;
LegacyRequest . Name = DebugContext ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-02-10 14:57:42 -05:00
LegacyRequest . Value = FLegacyCacheValue ( FCompositeBuffer ( FSharedBuffer : : Clone ( MakeMemoryView ( Data ) ) ) ) ;
2022-02-23 13:40:18 -05:00
FRequestOwner AsyncOwner ( EPriority : : Normal ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyPut ( { LegacyRequest } , AsyncOwner , [ ] ( auto & & ) { } ) ;
2022-02-23 13:40:18 -05:00
AsyncOwner . KeepAlive ( ) ;
2015-08-10 08:14:45 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_PutTime , bSynchronousForStats ? ( float ) ThisTime : 0.0f ) ;
}
}
if ( ! bSuccess )
{
Data . Empty ( ) ;
}
2022-02-23 13:47:00 -05:00
Backend - > AddToAsyncCompletionCounter ( - 1 ) ;
2020-09-24 00:43:27 -04:00
// Record that the task is finished and check that it was running and not finished or destroyed previously.
{
const uint32 PreviousState = WorkerState . fetch_xor ( WorkerStateRunning | WorkerStateFinished , std : : memory_order_relaxed ) ;
checkf ( ( PreviousState & WorkerStateRunning ) , TEXT ( " Finishing DDC worker that was not running! Key: %s " ) , * CacheKey ) ;
checkf ( ! ( PreviousState & WorkerStateFinished ) , TEXT ( " Finishing DDC worker that is already finished! Key: %s " ) , * CacheKey ) ;
checkf ( ! ( PreviousState & WorkerStateDestroyed ) , TEXT ( " Finishing DDC worker that has been destroyed! Key: %s " ) , * CacheKey ) ;
}
2015-08-10 08:14:45 -04:00
}
FORCEINLINE TStatId GetStatId ( ) const
{
RETURN_QUICK_DECLARE_CYCLE_STAT ( FBuildAsyncWorker , STATGROUP_ThreadPoolAsyncTasks ) ;
}
2020-09-24 00:43:27 -04:00
std : : atomic < uint32 > WorkerState { WorkerStateNone } ;
2015-08-10 08:14:45 -04:00
/** true in the case of a cache hit, otherwise the result of the deriver build call **/
bool bSuccess ;
/** true if we should record the timing **/
bool bSynchronousForStats ;
2016-05-16 16:20:52 -04:00
/** true if we had to build the data */
bool bDataWasBuilt ;
2022-02-23 13:47:00 -05:00
/** Backend graph to execute against. */
FDerivedDataBackend * Backend ;
2015-08-10 08:14:45 -04:00
/** Data dervier we are operating on **/
FDerivedDataPluginInterface * DataDeriver ;
/** Cache key associated with this build **/
FString CacheKey ;
2022-01-13 17:30:44 -05:00
/** Context from the caller */
FSharedString DebugContext ;
2015-08-10 08:14:45 -04:00
/** Data to return to caller, later **/
2022-02-11 12:56:47 -05:00
TArray64 < uint8 > Data ;
2015-08-10 08:14:45 -04:00
} ;
public :
/** Constructor, called once to cereate a singleton **/
FDerivedDataCache ( )
: CurrentHandle ( 19248 ) // we will skip some potential handles to catch errors
{
2022-01-18 04:45:24 -05:00
if ( FPlatformProcess : : SupportsMultithreading ( ) )
{
GCacheThreadPool = FQueuedThreadPool : : Allocate ( ) ;
const int32 ThreadCount = FPlatformMisc : : NumberOfIOWorkerThreadsToSpawn ( ) ;
2022-05-16 09:52:19 -04:00
# if WITH_EDITOR
// Use normal priority to avoid preempting GT/RT/RHI and other more important threads with CPU processing (i.e. compression) happening on the IO Threads in editor.
verify ( GCacheThreadPool - > Create ( ThreadCount , 96 * 1024 , TPri_Normal , TEXT ( " DDC IO ThreadPool " ) ) ) ;
# else
2022-01-18 04:45:24 -05:00
verify ( GCacheThreadPool - > Create ( ThreadCount , 96 * 1024 , TPri_AboveNormal , TEXT ( " DDC IO ThreadPool " ) ) ) ;
2022-05-16 09:52:19 -04:00
# endif
2022-01-18 04:45:24 -05:00
}
2022-02-23 13:47:00 -05:00
Backend = FDerivedDataBackend : : Create ( ) ;
2021-11-10 13:08:00 -05:00
CacheStoreMaintainers = IModularFeatures : : Get ( ) . GetModularFeatureImplementations < ICacheStoreMaintainer > ( FeatureName ) ;
2017-07-14 06:36:47 -04:00
GVerifyDDC = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " VerifyDDC " ) ) ;
2020-04-14 13:41:19 -04:00
UE_CLOG ( GVerifyDDC , LogDerivedDataCache , Display , TEXT ( " Items retrieved from the DDC will be verified (-VerifyDDC) " ) ) ;
2015-08-10 08:14:45 -04:00
}
/** Destructor, flushes all sync tasks **/
~ FDerivedDataCache ( )
{
2021-04-06 10:20:39 -04:00
WaitForQuiescence ( true ) ;
2015-08-10 08:14:45 -04:00
FScopeLock ScopeLock ( & SynchronizationObject ) ;
for ( TMap < uint32 , FAsyncTask < FBuildAsyncWorker > * > : : TIterator It ( PendingTasks ) ; It ; + + It )
{
It . Value ( ) - > EnsureCompletion ( ) ;
delete It . Value ( ) ;
}
PendingTasks . Empty ( ) ;
2022-02-23 13:47:00 -05:00
delete Backend ;
2015-08-10 08:14:45 -04:00
}
2022-02-11 12:56:47 -05:00
virtual bool GetSynchronous ( FDerivedDataPluginInterface * DataDeriver , TArray < uint8 > & OutData , bool * bDataWasBuilt ) override
2015-08-10 08:14:45 -04:00
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetSynchronous ) ;
2015-08-10 08:14:45 -04:00
check ( DataDeriver ) ;
FString CacheKey = FDerivedDataCache : : BuildCacheKey ( DataDeriver ) ;
2020-03-24 19:12:36 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " GetSynchronous %s from '%s' " ) , * CacheKey , * DataDeriver - > GetDebugContextString ( ) ) ;
2022-02-23 13:47:00 -05:00
FAsyncTask < FBuildAsyncWorker > PendingTask ( Backend , DataDeriver , * CacheKey , DataDeriver - > GetDebugContextString ( ) , true ) ;
2015-08-10 08:14:45 -04:00
AddToAsyncCompletionCounter ( 1 ) ;
PendingTask . StartSynchronousTask ( ) ;
2022-02-11 12:56:47 -05:00
OutData = TArray < uint8 > ( MoveTemp ( PendingTask . GetTask ( ) . Data ) ) ;
2016-05-16 16:20:52 -04:00
if ( bDataWasBuilt )
{
* bDataWasBuilt = PendingTask . GetTask ( ) . bDataWasBuilt ;
}
2015-08-10 08:14:45 -04:00
return PendingTask . GetTask ( ) . bSuccess ;
}
virtual uint32 GetAsynchronous ( FDerivedDataPluginInterface * DataDeriver ) override
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetAsynchronous ) ;
2015-08-10 08:14:45 -04:00
FScopeLock ScopeLock ( & SynchronizationObject ) ;
2019-11-25 12:03:09 -05:00
const uint32 Handle = NextHandle ( ) ;
2015-08-10 08:14:45 -04:00
FString CacheKey = FDerivedDataCache : : BuildCacheKey ( DataDeriver ) ;
2020-04-18 16:05:20 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " GetAsynchronous %s from '%s', Handle %d " ) , * CacheKey , * DataDeriver - > GetDebugContextString ( ) , Handle ) ;
2019-11-25 12:03:09 -05:00
const bool bSync = ! DataDeriver - > IsBuildThreadsafe ( ) ;
2022-02-23 13:47:00 -05:00
FAsyncTask < FBuildAsyncWorker > * AsyncTask = new FAsyncTask < FBuildAsyncWorker > ( Backend , DataDeriver , * CacheKey , DataDeriver - > GetDebugContextString ( ) , bSync ) ;
2015-08-10 08:14:45 -04:00
check ( ! PendingTasks . Contains ( Handle ) ) ;
PendingTasks . Add ( Handle , AsyncTask ) ;
AddToAsyncCompletionCounter ( 1 ) ;
if ( ! bSync )
{
2021-08-03 11:56:47 -04:00
AsyncTask - > StartBackgroundTask ( DataDeriver - > GetCustomThreadPool ( ) ) ;
2015-08-10 08:14:45 -04:00
}
else
{
AsyncTask - > StartSynchronousTask ( ) ;
}
// Must return a valid handle
check ( Handle ! = 0 ) ;
return Handle ;
}
virtual bool PollAsynchronousCompletion ( uint32 Handle ) override
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_PollAsynchronousCompletion ) ;
2015-08-10 08:14:45 -04:00
FAsyncTask < FBuildAsyncWorker > * AsyncTask = NULL ;
{
FScopeLock ScopeLock ( & SynchronizationObject ) ;
AsyncTask = PendingTasks . FindRef ( Handle ) ;
}
check ( AsyncTask ) ;
return AsyncTask - > IsDone ( ) ;
}
virtual void WaitAsynchronousCompletion ( uint32 Handle ) override
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_WaitAsynchronousCompletion ) ;
2015-08-10 08:14:45 -04:00
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
FAsyncTask < FBuildAsyncWorker > * AsyncTask = NULL ;
{
FScopeLock ScopeLock ( & SynchronizationObject ) ;
AsyncTask = PendingTasks . FindRef ( Handle ) ;
}
check ( AsyncTask ) ;
AsyncTask - > EnsureCompletion ( ) ;
2020-04-18 16:05:20 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " WaitAsynchronousCompletion, Handle %d " ) , Handle ) ;
2015-08-10 08:14:45 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_ASyncWaitTime , ( float ) ThisTime ) ;
}
2022-02-11 12:56:47 -05:00
template < typename DataType >
bool GetAsynchronousResultsByHandle ( uint32 Handle , DataType & OutData , bool * bOutDataWasBuilt )
2015-08-10 08:14:45 -04:00
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetAsynchronousResults ) ;
2015-08-10 08:14:45 -04:00
FAsyncTask < FBuildAsyncWorker > * AsyncTask = NULL ;
{
FScopeLock ScopeLock ( & SynchronizationObject ) ;
PendingTasks . RemoveAndCopyValue ( Handle , AsyncTask ) ;
}
check ( AsyncTask ) ;
2020-04-18 16:05:20 -04:00
const bool bDataWasBuilt = AsyncTask - > GetTask ( ) . bDataWasBuilt ;
if ( bOutDataWasBuilt )
2016-05-16 16:20:52 -04:00
{
2020-04-18 16:05:20 -04:00
* bOutDataWasBuilt = bDataWasBuilt ;
2016-05-16 16:20:52 -04:00
}
2015-08-10 08:14:45 -04:00
if ( ! AsyncTask - > GetTask ( ) . bSuccess )
{
2020-04-18 16:05:20 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " GetAsynchronousResults, bDataWasBuilt: %d, Handle %d, FAILED " ) , ( int32 ) bDataWasBuilt , Handle ) ;
2015-08-10 08:14:45 -04:00
delete AsyncTask ;
return false ;
}
2020-04-18 16:05:20 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " GetAsynchronousResults, bDataWasBuilt: %d, Handle %d, SUCCESS " ) , ( int32 ) bDataWasBuilt , Handle ) ;
2022-02-11 12:56:47 -05:00
OutData = DataType ( MoveTemp ( AsyncTask - > GetTask ( ) . Data ) ) ;
2015-08-10 08:14:45 -04:00
delete AsyncTask ;
check ( OutData . Num ( ) ) ;
return true ;
}
2022-02-11 12:56:47 -05:00
virtual bool GetAsynchronousResults ( uint32 Handle , TArray < uint8 > & OutData , bool * bOutDataWasBuilt ) override
{
return GetAsynchronousResultsByHandle ( Handle , OutData , bOutDataWasBuilt ) ;
}
virtual bool GetAsynchronousResults ( uint32 Handle , TArray64 < uint8 > & OutData , bool * bOutDataWasBuilt ) override
{
return GetAsynchronousResultsByHandle ( Handle , OutData , bOutDataWasBuilt ) ;
}
template < typename DataType >
bool GetSynchronousByKey ( const TCHAR * CacheKey , DataType & OutData , FStringView DebugContext )
2015-08-10 08:14:45 -04:00
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetSynchronous_Data ) ;
2022-01-13 17:30:44 -05:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " GetSynchronous %s from '%.*s' " ) , CacheKey , DebugContext . Len ( ) , DebugContext . GetData ( ) ) ;
2022-02-23 13:47:00 -05:00
FAsyncTask < FBuildAsyncWorker > PendingTask ( Backend , nullptr , CacheKey , DebugContext , true ) ;
2015-08-10 08:14:45 -04:00
AddToAsyncCompletionCounter ( 1 ) ;
PendingTask . StartSynchronousTask ( ) ;
2022-02-11 12:56:47 -05:00
OutData = DataType ( MoveTemp ( PendingTask . GetTask ( ) . Data ) ) ;
2015-08-10 08:14:45 -04:00
return PendingTask . GetTask ( ) . bSuccess ;
}
2022-02-11 12:56:47 -05:00
virtual bool GetSynchronous ( const TCHAR * CacheKey , TArray < uint8 > & OutData , FStringView DebugContext ) override
{
return GetSynchronousByKey ( CacheKey , OutData , DebugContext ) ;
}
virtual bool GetSynchronous ( const TCHAR * CacheKey , TArray64 < uint8 > & OutData , FStringView DebugContext ) override
{
return GetSynchronousByKey ( CacheKey , OutData , DebugContext ) ;
}
2022-01-13 17:30:44 -05:00
virtual uint32 GetAsynchronous ( const TCHAR * CacheKey , FStringView DebugContext ) override
2015-08-10 08:14:45 -04:00
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetAsynchronous_Handle ) ;
2015-08-10 08:14:45 -04:00
FScopeLock ScopeLock ( & SynchronizationObject ) ;
2019-11-25 12:03:09 -05:00
const uint32 Handle = NextHandle ( ) ;
2022-01-13 17:30:44 -05:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " GetAsynchronous %s from '%.*s', Handle %d " ) , CacheKey , DebugContext . Len ( ) , DebugContext . GetData ( ) , Handle ) ;
2022-02-23 13:47:00 -05:00
FAsyncTask < FBuildAsyncWorker > * AsyncTask = new FAsyncTask < FBuildAsyncWorker > ( Backend , nullptr , CacheKey , DebugContext , false ) ;
2015-08-10 08:14:45 -04:00
check ( ! PendingTasks . Contains ( Handle ) ) ;
PendingTasks . Add ( Handle , AsyncTask ) ;
AddToAsyncCompletionCounter ( 1 ) ;
2021-01-25 10:55:26 -04:00
// This request is I/O only, doesn't do any processing, send it to the I/O only thread-pool to avoid wasting worker threads on long I/O waits.
2022-01-18 04:45:24 -05:00
AsyncTask - > StartBackgroundTask ( GCacheThreadPool ) ;
2015-08-10 08:14:45 -04:00
return Handle ;
}
2022-02-11 12:56:47 -05:00
virtual void Put ( const TCHAR * CacheKey , TArrayView64 < const uint8 > Data , FStringView DebugContext , bool bPutEvenIfExists = false ) override
2015-08-10 08:14:45 -04:00
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_Put ) ;
2022-01-13 17:30:44 -05:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " Put %s from '%.*s' " ) , CacheKey , DebugContext . Len ( ) , DebugContext . GetData ( ) ) ;
2015-08-10 08:14:45 -04:00
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
2022-01-13 17:30:44 -05:00
FLegacyCachePutRequest LegacyRequest ;
LegacyRequest . Name = DebugContext ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-02-10 14:57:42 -05:00
LegacyRequest . Value = FLegacyCacheValue ( FCompositeBuffer ( FSharedBuffer : : Clone ( MakeMemoryView ( Data ) ) ) ) ;
2022-02-23 13:40:18 -05:00
FRequestOwner AsyncOwner ( EPriority : : Normal ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyPut ( { LegacyRequest } , AsyncOwner , [ ] ( auto & & ) { } ) ;
2022-02-23 13:40:18 -05:00
AsyncOwner . KeepAlive ( ) ;
2015-08-10 08:14:45 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_PutTime , ( float ) ThisTime ) ;
INC_DWORD_STAT ( STAT_DDC_NumPuts ) ;
}
virtual void MarkTransient ( const TCHAR * CacheKey ) override
{
2022-03-16 15:28:29 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_MarkTransient ) ;
2022-01-13 17:30:44 -05:00
FLegacyCacheDeleteRequest LegacyRequest ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-01-13 17:30:44 -05:00
LegacyRequest . Name = LegacyRequest . Key . GetFullKey ( ) ;
LegacyRequest . bTransient = true ;
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyDelete ( { LegacyRequest } , BlockingOwner , [ ] ( auto & & ) { } ) ;
2022-01-13 17:30:44 -05:00
BlockingOwner . Wait ( ) ;
2015-08-10 08:14:45 -04:00
}
virtual bool CachedDataProbablyExists ( const TCHAR * CacheKey ) override
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_CachedDataProbablyExists ) ;
2015-08-10 08:14:45 -04:00
bool bResult ;
INC_DWORD_STAT ( STAT_DDC_NumExist ) ;
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
2022-01-13 17:30:44 -05:00
FLegacyCacheGetRequest LegacyRequest ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-01-13 17:30:44 -05:00
LegacyRequest . Name = LegacyRequest . Key . GetFullKey ( ) ;
LegacyRequest . Policy = ECachePolicy : : Query | ECachePolicy : : SkipData ;
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyGet ( { LegacyRequest } , BlockingOwner ,
2022-01-13 17:30:44 -05:00
[ & bResult ] ( FLegacyCacheGetResponse & & Response ) { bResult = Response . Status = = EStatus : : Ok ; } ) ;
BlockingOwner . Wait ( ) ;
2015-08-10 08:14:45 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_ExistTime , ( float ) ThisTime ) ;
return bResult ;
}
2021-01-06 09:27:11 -04:00
virtual TBitArray < > CachedDataProbablyExistsBatch ( TConstArrayView < FString > CacheKeys ) override
{
2022-01-13 17:30:44 -05:00
TBitArray < > Result ( false , CacheKeys . Num ( ) ) ;
if ( ! CacheKeys . IsEmpty ( ) )
2021-01-06 09:27:11 -04:00
{
2021-01-08 11:44:45 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_CachedDataProbablyExistsBatch ) ;
2021-01-06 09:27:11 -04:00
INC_DWORD_STAT ( STAT_DDC_NumExist ) ;
STAT ( double ThisTime = 0 ) ;
{
SCOPE_SECONDS_COUNTER ( ThisTime ) ;
2022-01-13 17:30:44 -05:00
TArray < FLegacyCacheGetRequest , TInlineAllocator < 8 > > LegacyRequests ;
int32 Index = 0 ;
for ( const FString & CacheKey : CacheKeys )
{
FLegacyCacheGetRequest & LegacyRequest = LegacyRequests . AddDefaulted_GetRef ( ) ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-01-13 17:30:44 -05:00
LegacyRequest . Name = LegacyRequest . Key . GetFullKey ( ) ;
LegacyRequest . Policy = ECachePolicy : : Query | ECachePolicy : : SkipData ;
LegacyRequest . UserData = uint64 ( Index ) ;
+ + Index ;
}
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyGet ( LegacyRequests , BlockingOwner ,
2022-01-13 17:30:44 -05:00
[ & Result ] ( FLegacyCacheGetResponse & & Response )
{
Result [ int32 ( Response . UserData ) ] = Response . Status = = EStatus : : Ok ;
} ) ;
BlockingOwner . Wait ( ) ;
2021-01-06 09:27:11 -04:00
}
INC_FLOAT_STAT_BY ( STAT_DDC_ExistTime , ( float ) ThisTime ) ;
}
return Result ;
}
virtual bool AllCachedDataProbablyExists ( TConstArrayView < FString > CacheKeys ) override
{
return CacheKeys . Num ( ) = = 0 | | CachedDataProbablyExistsBatch ( CacheKeys ) . CountSetBits ( ) = = CacheKeys . Num ( ) ;
}
2021-02-16 17:24:48 -04:00
virtual bool TryToPrefetch ( TConstArrayView < FString > CacheKeys , FStringView DebugContext ) override
{
2021-02-18 10:13:14 -04:00
if ( ! CacheKeys . IsEmpty ( ) )
2021-02-16 17:24:48 -04:00
{
DDC_SCOPE_CYCLE_COUNTER ( DDC_TryToPrefetch ) ;
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " TryToPrefetch %d keys including %s from '%.*s' " ) ,
2022-01-13 17:30:44 -05:00
CacheKeys . Num ( ) , * CacheKeys [ 0 ] , DebugContext . Len ( ) , DebugContext . GetData ( ) ) ;
TArray < FLegacyCacheGetRequest , TInlineAllocator < 8 > > LegacyRequests ;
int32 Index = 0 ;
const FSharedString Name = DebugContext ;
for ( const FString & CacheKey : CacheKeys )
{
FLegacyCacheGetRequest & LegacyRequest = LegacyRequests . AddDefaulted_GetRef ( ) ;
LegacyRequest . Name = Name ;
2022-02-23 13:47:00 -05:00
LegacyRequest . Key = FLegacyCacheKey ( CacheKey , Backend - > GetMaxKeyLength ( ) ) ;
2022-01-13 17:30:44 -05:00
LegacyRequest . Policy = ECachePolicy : : Default | ECachePolicy : : SkipData ;
LegacyRequest . UserData = uint64 ( Index ) ;
+ + Index ;
}
bool bOk = true ;
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
2022-02-23 13:47:00 -05:00
Backend - > GetRoot ( ) . LegacyGet ( LegacyRequests , BlockingOwner ,
2022-01-13 17:30:44 -05:00
[ & bOk ] ( FLegacyCacheGetResponse & & Response )
{
bOk & = Response . Status = = EStatus : : Ok ;
} ) ;
BlockingOwner . Wait ( ) ;
return bOk ;
2021-02-16 17:24:48 -04:00
}
return true ;
}
2015-08-10 08:14:45 -04:00
void NotifyBootComplete ( ) override
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_NotifyBootComplete ) ;
2022-02-23 13:47:00 -05:00
Backend - > NotifyBootComplete ( ) ;
2015-08-10 08:14:45 -04:00
}
void AddToAsyncCompletionCounter ( int32 Addend ) override
{
2022-02-23 13:47:00 -05:00
Backend - > AddToAsyncCompletionCounter ( Addend ) ;
2015-08-10 08:14:45 -04:00
}
2021-03-18 15:20:03 -04:00
bool AnyAsyncRequestsRemaining ( ) const override
{
2022-02-23 13:47:00 -05:00
return Backend - > AnyAsyncRequestsRemaining ( ) ;
2021-03-18 15:20:03 -04:00
}
2015-08-10 08:14:45 -04:00
void WaitForQuiescence ( bool bShutdown ) override
{
2019-06-03 15:32:00 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_WaitForQuiescence ) ;
2022-02-23 13:47:00 -05:00
Backend - > WaitForQuiescence ( bShutdown ) ;
2015-08-10 08:14:45 -04:00
}
2017-11-06 18:22:01 -05:00
/** Get whether a Shared Data Cache is in use */
virtual bool GetUsingSharedDDC ( ) const override
{
2022-02-23 13:47:00 -05:00
return Backend - > GetUsingSharedDDC ( ) ;
2017-11-06 18:22:01 -05:00
}
2020-09-24 00:43:27 -04:00
virtual const TCHAR * GetGraphName ( ) const override
{
2022-02-23 13:47:00 -05:00
return Backend - > GetGraphName ( ) ;
2020-09-24 00:43:27 -04:00
}
2021-03-18 15:20:03 -04:00
virtual const TCHAR * GetDefaultGraphName ( ) const override
{
2022-02-23 13:47:00 -05:00
return Backend - > GetDefaultGraphName ( ) ;
2021-03-18 15:20:03 -04:00
}
2015-08-10 08:14:45 -04:00
void GetDirectories ( TArray < FString > & OutResults ) override
{
2022-02-23 13:47:00 -05:00
Backend - > GetDirectories ( OutResults ) ;
2015-08-10 08:14:45 -04:00
}
2021-11-10 13:08:00 -05:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-03-10 17:33:38 -04:00
virtual IDDCCleanup * GetCleanup ( ) const override
{
2021-11-10 13:08:00 -05:00
return const_cast < IDDCCleanup * > ( static_cast < const IDDCCleanup * > ( this ) ) ;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
virtual bool IsFinished ( ) const override
{
return IsIdle ( ) ;
}
virtual void WaitBetweenDeletes ( bool bWait ) override
{
if ( ! bWait )
{
BoostPriority ( ) ;
}
2021-03-10 17:33:38 -04:00
}
2021-06-22 00:27:54 -04:00
virtual void GatherUsageStats ( TMap < FString , FDerivedDataCacheUsageStats > & UsageStats ) override
{
GatherUsageStats ( ) - > GatherLegacyUsageStats ( UsageStats , TEXT ( " 0 " ) ) ;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-03-18 15:20:03 -04:00
virtual TSharedRef < FDerivedDataCacheStatsNode > GatherUsageStats ( ) const override
2016-02-19 12:03:17 -05:00
{
2022-02-23 13:47:00 -05:00
return Backend - > GatherUsageStats ( ) ;
2016-02-19 12:03:17 -05:00
}
2021-06-22 00:27:54 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2015-08-10 08:14:45 -04:00
2021-07-29 13:07:00 -04:00
virtual void GatherResourceStats ( TArray < FDerivedDataCacheResourceStat > & DDCResourceStats ) const override
{
GatherDerivedDataCacheResourceStats ( DDCResourceStats ) ;
}
virtual void GatherSummaryStats ( FDerivedDataCacheSummaryStats & DDCSummaryStats ) const override
{
GatherDerivedDataCacheSummaryStats ( DDCSummaryStats ) ;
}
2017-11-06 18:22:01 -05:00
/** Get event delegate for data cache notifications */
virtual FOnDDCNotification & GetDDCNotificationEvent ( )
{
return DDCNotificationEvent ;
}
2015-08-10 08:14:45 -04:00
protected :
uint32 NextHandle ( )
{
return ( uint32 ) CurrentHandle . Increment ( ) ;
}
private :
/**
* Internal function to build a cache key out of the plugin name, versions and plugin specific info
* @param DataDeriver plugin to produce the elements of the cache key.
* @return Assembled cache key
**/
static FString BuildCacheKey ( FDerivedDataPluginInterface * DataDeriver )
{
FString Result = FDerivedDataCacheInterface : : BuildCacheKey ( DataDeriver - > GetPluginName ( ) , DataDeriver - > GetVersionString ( ) , * DataDeriver - > GetPluginSpecificCacheKeySuffix ( ) ) ;
return Result ;
}
2022-02-23 13:47:00 -05:00
FDerivedDataBackend * Backend ;
2015-08-10 08:14:45 -04:00
/** Counter used to produce unique handles **/
FThreadSafeCounter CurrentHandle ;
/** Object used for synchronization via a scoped lock **/
FCriticalSection SynchronizationObject ;
/** Map of handle to pending task **/
TMap < uint32 , FAsyncTask < FBuildAsyncWorker > * > PendingTasks ;
2017-11-06 18:22:01 -05:00
/** Cache notification delegate */
FOnDDCNotification DDCNotificationEvent ;
2021-01-21 01:57:01 -04:00
2015-08-10 08:14:45 -04:00
public :
2022-01-27 15:43:52 -05:00
// ICache Interface
2021-05-03 09:25:48 -04:00
2021-08-04 18:08:50 -04:00
void Put (
2021-12-13 13:32:28 -05:00
TConstArrayView < FCachePutRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2021-04-28 16:22:18 -04:00
FOnCachePutComplete & & OnComplete ) final
{
2022-03-16 15:28:29 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_Put ) ;
2022-08-16 19:23:33 -04:00
return Backend - > GetRoot ( ) . Put ( Requests , Owner , OnComplete ? MoveTemp ( OnComplete ) : FOnCachePutComplete ( [ ] ( auto & & ) { } ) ) ;
2021-04-28 16:22:18 -04:00
}
2021-02-19 11:26:34 -04:00
2021-11-10 13:08:00 -05:00
void Get (
2021-12-13 13:32:28 -05:00
TConstArrayView < FCacheGetRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2021-04-28 16:22:18 -04:00
FOnCacheGetComplete & & OnComplete ) final
{
2022-03-16 15:28:29 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_Get ) ;
2022-08-16 19:23:33 -04:00
return Backend - > GetRoot ( ) . Get ( Requests , Owner , OnComplete ? MoveTemp ( OnComplete ) : FOnCacheGetComplete ( [ ] ( auto & & ) { } ) ) ;
2021-04-28 16:22:18 -04:00
}
2021-01-21 01:57:01 -04:00
2022-01-10 12:13:18 -05:00
void PutValue (
TConstArrayView < FCachePutValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCachePutValueComplete & & OnComplete ) final
{
2022-03-16 15:28:29 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_PutValue ) ;
2022-08-16 19:23:33 -04:00
return Backend - > GetRoot ( ) . PutValue ( Requests , Owner , OnComplete ? MoveTemp ( OnComplete ) : FOnCachePutValueComplete ( [ ] ( auto & & ) { } ) ) ;
2022-01-10 12:13:18 -05:00
}
void GetValue (
TConstArrayView < FCacheGetValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCacheGetValueComplete & & OnComplete ) final
{
2022-03-16 15:28:29 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetValue ) ;
2022-08-16 19:23:33 -04:00
return Backend - > GetRoot ( ) . GetValue ( Requests , Owner , OnComplete ? MoveTemp ( OnComplete ) : FOnCacheGetValueComplete ( [ ] ( auto & & ) { } ) ) ;
2022-01-10 12:13:18 -05:00
}
2021-10-07 09:11:32 -04:00
void GetChunks (
2022-01-18 04:47:59 -05:00
TConstArrayView < FCacheGetChunkRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2022-01-18 04:47:59 -05:00
FOnCacheGetChunkComplete & & OnComplete ) final
2021-01-21 01:57:01 -04:00
{
2022-03-16 15:28:29 -04:00
DDC_SCOPE_CYCLE_COUNTER ( DDC_GetChunks ) ;
2022-08-16 19:23:33 -04:00
return Backend - > GetRoot ( ) . GetChunks ( Requests , Owner , OnComplete ? MoveTemp ( OnComplete ) : FOnCacheGetChunkComplete ( [ ] ( auto & & ) { } ) ) ;
2021-01-21 01:57:01 -04:00
}
2021-11-10 13:08:00 -05:00
ICacheStoreMaintainer & GetMaintainer ( ) final
{
return * this ;
}
// ICacheStoreMaintainer Interface
bool IsIdle ( ) const final
{
return Algo : : AllOf ( CacheStoreMaintainers , & ICacheStoreMaintainer : : IsIdle ) ;
}
void BoostPriority ( ) final
{
for ( ICacheStoreMaintainer * Maintainer : CacheStoreMaintainers )
{
Maintainer - > BoostPriority ( ) ;
}
}
private :
TArray < ICacheStoreMaintainer * > CacheStoreMaintainers ;
2021-01-21 01:57:01 -04:00
} ;
2021-08-05 13:12:08 -04:00
ICache * CreateCache ( FDerivedDataCacheInterface * * OutLegacyCache )
2021-04-28 23:25:28 -04:00
{
2021-08-05 13:12:08 -04:00
FDerivedDataCache * Cache = new FDerivedDataCache ;
if ( OutLegacyCache )
{
* OutLegacyCache = Cache ;
}
return Cache ;
2021-05-21 12:24:43 -04:00
}
2021-04-28 23:25:28 -04:00
2021-05-21 12:24:43 -04:00
} // UE::DerivedData::Private