2024-06-11 16:46:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "RequiredProgramMainCPPInclude.h"
# include "Algo/Sort.h"
# include "AssetRegistry/AssetData.h"
# include "AssetRegistry/AssetDataTagMap.h"
# include "AssetRegistry/AssetRegistryState.h"
# include "AssetRegistry/IAssetRegistry.h"
# include "Containers/Array.h"
# include "Containers/ArrayView.h"
# include "Containers/ContainersFwd.h"
# include "Containers/Map.h"
# include "HAL/PlatformCrt.h"
# include "IO/IoDispatcher.h"
# include "IO/IoHash.h"
# include "Misc/CString.h"
# include "Misc/Parse.h"
# include "Templates/UnrealTemplate.h"
# include "Trace/Detail/Channel.h"
# include "UObject/NameTypes.h"
# include "UObject/TopLevelAssetPath.h"
IMPLEMENT_APPLICATION ( DiffAssetBulkData , " DiffAssetBulkData " ) ;
DEFINE_LOG_CATEGORY_STATIC ( LogDiffAssetBulk , Display , All ) ;
/**
* Diff Asset Bulk Data
*
* This loads two asset registries newer than FAssetRegistryVersion : : AddedChunkHashes ,
* and attempts to find the reason for bulk data differences .
*
* First , it finds what bulk datas changed by using the hash of the bulk data ,
* then it uses " Diff Tags " to try and determine at what point during the derived data
* build the change occurred .
*
*
* Diff Tags
*
* Diff Tags are cook tags added during the cook process using Ar . CookContext ( ) - > CookTagList ( ) ( see CookTagList . h )
* and are of the form " Cook_Diff_##_Key " :
*
* - " Cook_ " : Added automatically by the the cook tag system .
* - " Diff_ " : Identifies the tag as a diff tag .
* - " ## " : Specifies where in the build process the tag represents ( Ordering ) .
* - " _Key " : Descriptive text for the tag .
*
* If a bulk data difference is found , the diff tags are checked for differences in order , and the first
* diff tag that changed is assigned the " blame " for the change under the assumption that later
* tags will necessarily change as a result of the earlier change .
*
* If diff tags are present for the asset and none of the diff tags changed , then it is assumed that a build determinism
* issue has caused the change .
*
*/
/**
* The list of known cook diff tags - this is just used to provide explanations in the output for the reader .
*/
static struct FBuiltinDiffTagHelp { const TCHAR * TagName ; const TCHAR * TagHelp ; } GBuiltinDiffTagHelp [ ] =
{
{ TEXT ( " Cook_Diff_20_Tex2D_CacheKey " ) , TEXT ( " Texture settings or referenced data changed (DDC2) " ) } ,
{ TEXT ( " Cook_Diff_20_Tex2D_DDK " ) , TEXT ( " Texture settings or referenced data changed (DDC1) " ) } ,
{ TEXT ( " Cook_Diff_10_Tex2D_Source " ) , TEXT ( " Texture source data changed " ) }
} ;
static int32 RunDiffAssetBulkData ( )
{
FString BaseFileName , CurrentFileName ;
const TCHAR * CmdLine = FCommandLine : : Get ( ) ;
if ( FParse : : Value ( CmdLine , TEXT ( " Base= " ) , BaseFileName ) = = false | |
FParse : : Value ( CmdLine , TEXT ( " Current= " ) , CurrentFileName ) = = false )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Diff Asset Bulk Data " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Loads two development asset registries and finds all bulk data changes, and tries to find why " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " the bulk data changed. Development asset registries are in the cooked /Metadata directory. " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Parameters: " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -Base=<path/to/file> Base Development Asset Registry (Required) " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -Current=<path/to/file> New Development Asset Registry (Required) " ) ) ;
2024-06-17 16:41:27 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -Optional Evaluate Optional bulk data changes instead. " ) ) ;
2024-06-11 16:46:57 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -ListMixed Show the list of changed packages with assets that have matching " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " blame tags, but also assets without. " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -ListDeterminism Show the list of changed packages with assets that have matching " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " blame tags. " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -ListBlame=<blame tag> Show the list of assets that changed due to a specific blame " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " tag or \" All \" to list all changed assets with known blame. " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -ListUnrepresented Show the list of packages where a representative asset couldn't be found. " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -ListNoBlame=<class> Show the list of assets that changed for a specific class, or \" All \" " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " -ListCSV=<filename> Write all changed packages to the given CSV file. " ) ) ;
return 1 ;
}
2024-06-17 16:41:27 -04:00
bool bEvaluateOptional = FParse : : Param ( CmdLine , TEXT ( " Optional " ) ) ;
2024-06-11 16:46:57 -04:00
bool bListMixed = FParse : : Param ( CmdLine , TEXT ( " ListMixed " ) ) ;
bool bListDeterminism = FParse : : Param ( CmdLine , TEXT ( " ListDeterminism " ) ) ;
bool bListUnrepresented = FParse : : Param ( CmdLine , TEXT ( " ListUnrepresented " ) ) ;
FString ListBlame ;
FParse : : Value ( CmdLine , TEXT ( " ListBlame= " ) , ListBlame ) ;
FString ListNoBlame ;
FParse : : Value ( CmdLine , TEXT ( " ListNoBlame= " ) , ListNoBlame ) ;
FString ListCSV ;
2024-06-17 16:41:27 -04:00
TUniquePtr < FArchive > ChangedCSVAr ;
TUniquePtr < FArchive > NewCSVAr ;
TUniquePtr < FArchive > MovedCSVAr ;
TUniquePtr < FArchive > DeletedCSVAr ;
2024-06-11 16:46:57 -04:00
if ( FParse : : Value ( CmdLine , TEXT ( " ListCSV= " ) , ListCSV ) )
{
2024-06-17 16:41:27 -04:00
FString Extension = FPaths : : GetExtension ( ListCSV ) ;
FString Base = FPaths : : ChangeExtension ( ListCSV , TEXT ( " " ) ) ;
ChangedCSVAr . Reset ( IFileManager : : Get ( ) . CreateFileWriter ( * ( Base + TEXT ( " Changed. " ) + Extension ) , 0 ) ) ;
if ( ! ChangedCSVAr )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Unable to open output CSV file: %s " ) , * ( Base + TEXT ( " Changed. " ) + Extension ) ) ;
2024-06-11 16:46:57 -04:00
return false ;
}
2024-06-26 17:16:24 -04:00
ChangedCSVAr - > Logf ( TEXT ( " Blame, Class, PackageName, BlameBefore, BlameAfter, OldCompressedSize, NewCompressedSize, OldUncompressedSize, NewUncompressedSize " ) ) ;
2024-06-17 16:41:27 -04:00
NewCSVAr . Reset ( IFileManager : : Get ( ) . CreateFileWriter ( * ( Base + TEXT ( " New. " ) + Extension ) , 0 ) ) ;
if ( ! NewCSVAr )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Unable to open output CSV file: %s " ) , * ( Base + TEXT ( " New. " ) + Extension ) ) ;
return false ;
}
NewCSVAr - > Logf ( TEXT ( " Class, PackageName " ) ) ;
DeletedCSVAr . Reset ( IFileManager : : Get ( ) . CreateFileWriter ( * ( Base + TEXT ( " Deleted. " ) + Extension ) , 0 ) ) ;
if ( ! DeletedCSVAr )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Unable to open output CSV file: %s " ) , * ( Base + TEXT ( " Deleted. " ) + Extension ) ) ;
return false ;
}
DeletedCSVAr - > Logf ( TEXT ( " Class, PackageName " ) ) ;
MovedCSVAr . Reset ( IFileManager : : Get ( ) . CreateFileWriter ( * ( Base + TEXT ( " Moved. " ) + Extension ) , 0 ) ) ;
if ( ! MovedCSVAr )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Unable to open output CSV file: %s " ) , * ( Base + TEXT ( " Moved. " ) + Extension ) ) ;
return false ;
}
MovedCSVAr - > Logf ( TEXT ( " Class, PackageName " ) ) ;
2024-06-11 16:46:57 -04:00
}
// Convert the static init help text to a map
TMap < FName , const TCHAR * > BuiltinDiffTagHelpMap ;
for ( FBuiltinDiffTagHelp & DiffTagHelp : GBuiltinDiffTagHelp )
{
BuiltinDiffTagHelpMap . Add ( DiffTagHelp . TagName , DiffTagHelp . TagHelp ) ;
}
FAssetRegistryState BaseState , CurrentState ;
FAssetRegistryVersion : : Type BaseVersion , CurrentVersion ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Loading Base... (%s) " ) , * BaseFileName ) ;
if ( FAssetRegistryState : : LoadFromDisk ( * BaseFileName , FAssetRegistryLoadOptions ( ) , BaseState , & BaseVersion ) = = false )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Failed load base (%s) " ) , * BaseFileName ) ;
return 1 ;
}
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Loading Current... (%s) " ) , * CurrentFileName ) ;
if ( FAssetRegistryState : : LoadFromDisk ( * CurrentFileName , FAssetRegistryLoadOptions ( ) , CurrentState , & CurrentVersion ) = = false )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Failed load current (%s) " ) , * CurrentFileName ) ;
return 1 ;
}
//
// The cook process adds the hash for almost all iochunks to the asset registry -
// so as long as both asset registries have that data, we get what we want.
//
if ( BaseVersion < FAssetRegistryVersion : : AddedChunkHashes )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Base asset registry version is too old (%d, need %d) " ) , BaseVersion , FAssetRegistryVersion : : AddedChunkHashes ) ;
return 1 ;
}
if ( CurrentVersion < FAssetRegistryVersion : : AddedChunkHashes )
{
UE_LOG ( LogDiffAssetBulk , Error , TEXT ( " Current asset registry version is too old (%d, need %d) " ) , CurrentVersion , FAssetRegistryVersion : : AddedChunkHashes ) ;
return 1 ;
}
const TMap < FName , const FAssetPackageData * > & BasePackages = BaseState . GetAssetPackageDataMap ( ) ;
const TMap < FName , const FAssetPackageData * > & CurrentPackages = CurrentState . GetAssetPackageDataMap ( ) ;
struct FIteratedPackage
{
FName Name = NAME_None ;
const FAssetPackageData * Base = nullptr ;
const FAssetPackageData * Current = nullptr ;
FIteratedPackage ( ) = default ;
FIteratedPackage ( FName _Name , const FAssetPackageData * _Base , const FAssetPackageData * _Current ) :
Name ( _Name ) ,
Base ( _Base ) ,
Current ( _Current ) { }
} ;
TArray < FIteratedPackage > UnionedPackages ;
uint64 CurrentTotalSize = 0 ;
uint64 BaseTotalSize = 0 ;
{
for ( const TPair < FName , const FAssetPackageData * > & NamePackageDataPair : BasePackages )
{
const FAssetPackageData * Current = CurrentState . GetAssetPackageData ( NamePackageDataPair . Key ) ;
2024-07-27 15:28:25 -04:00
const FAssetData * BaseMIAsset = UE : : AssetRegistry : : GetMostImportantAsset ( BaseState . CopyAssetsByPackageName ( NamePackageDataPair . Key ) , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : IgnoreSkipClasses ) ;
2024-06-11 16:46:57 -04:00
uint64 BaseCompressedSize = 0 ;
if ( BaseMIAsset & & BaseMIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkCompressedSizeFName , BaseCompressedSize ) )
{
BaseTotalSize + = BaseCompressedSize ;
}
UnionedPackages . Emplace ( FIteratedPackage ( NamePackageDataPair . Key , NamePackageDataPair . Value , Current ) ) ;
}
for ( const TPair < FName , const FAssetPackageData * > & NamePackageDataPair : CurrentPackages )
{
const FAssetPackageData * Base = BaseState . GetAssetPackageData ( NamePackageDataPair . Key ) ;
2024-07-27 15:28:25 -04:00
const FAssetData * CurrentMIAsset = UE : : AssetRegistry : : GetMostImportantAsset ( CurrentState . CopyAssetsByPackageName ( NamePackageDataPair . Key ) , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : IgnoreSkipClasses ) ;
2024-06-11 16:46:57 -04:00
uint64 CurrentCompressedSize = 0 ;
if ( CurrentMIAsset & & CurrentMIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkCompressedSizeFName , CurrentCompressedSize ) )
{
CurrentTotalSize + = CurrentCompressedSize ;
}
if ( Base = = nullptr )
{
UnionedPackages . Emplace ( FIteratedPackage ( NamePackageDataPair . Key , nullptr , NamePackageDataPair . Value ) ) ;
}
}
}
// Now we need to see what changed.
//
// This whole thing assumes that the index parameter of CreateIoChunkId is always 0. This is likely not going
// to be true with FDerivedData, once that gets turned on, but should be easy to update when the time comes.
//
2024-06-17 16:41:27 -04:00
// Save off what hashes got deleted so we can try to find packages that moved and report those separately.
TMap < FIoHash , TArray < FName , TInlineAllocator < 1 > > > DeletedChunkPackagesByHash ;
TSet < FName > PackagesWithChangedChunks ;
TSet < FName > PackagesWithDeletedChunks ;
TMap < FName , TArray < FIoHash , TInlineAllocator < 1 > > > PackagesWithNewChunks ;
auto ShouldProcessChunk = [ bEvaluateOptional ] ( const FIoChunkId & ChunkId )
{
if ( ChunkId . GetChunkType ( ) ! = EIoChunkType : : BulkData & &
ChunkId . GetChunkType ( ) ! = EIoChunkType : : OptionalBulkData & &
ChunkId . GetChunkType ( ) ! = EIoChunkType : : MemoryMappedBulkData )
{
return false ;
}
bool bIsOptional = ChunkId . GetChunkType ( ) = = EIoChunkType : : OptionalBulkData ;
if ( bEvaluateOptional )
{
return bIsOptional ;
}
return ! bIsOptional ;
} ;
2024-06-26 17:16:24 -04:00
struct FPackageSizes
{
uint64 BaseCompressedSize = 0 ;
uint64 CurrentCompressedSize = 0 ;
uint64 BaseUncompressedSize = 0 ;
uint64 CurrentUncompressedSize = 0 ;
} ;
2024-06-17 16:41:27 -04:00
uint64 TotalChangedSize = 0 ;
2024-06-26 17:16:24 -04:00
TMap < FName /* PackageName */ , FPackageSizes > PackageSizes ;
2024-06-11 16:46:57 -04:00
for ( const FIteratedPackage & IteratedPackage : UnionedPackages )
{
const FAssetPackageData * BasePackage = IteratedPackage . Base ;
const FAssetPackageData * CurrentPackage = IteratedPackage . Current ;
2024-06-17 16:41:27 -04:00
// Get the size change.
// IoStoreUtilities puts the size of the package on the most important asset
2024-07-27 15:28:25 -04:00
const FAssetData * BaseMIAsset = UE : : AssetRegistry : : GetMostImportantAsset ( BaseState . CopyAssetsByPackageName ( IteratedPackage . Name ) , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : IgnoreSkipClasses ) ;
const FAssetData * CurrentMIAsset = UE : : AssetRegistry : : GetMostImportantAsset ( CurrentState . CopyAssetsByPackageName ( IteratedPackage . Name ) , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : IgnoreSkipClasses ) ;
2024-06-17 16:41:27 -04:00
2024-06-26 17:16:24 -04:00
FPackageSizes & Sizes = PackageSizes . Add ( IteratedPackage . Name ) ;
2024-06-17 16:41:27 -04:00
if ( BaseMIAsset )
{
2024-06-26 17:16:24 -04:00
BaseMIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkCompressedSizeFName , Sizes . BaseCompressedSize ) ;
BaseMIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkSizeFName , Sizes . BaseUncompressedSize ) ;
2024-06-17 16:41:27 -04:00
}
if ( CurrentMIAsset )
{
2024-06-26 17:16:24 -04:00
CurrentMIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkCompressedSizeFName , Sizes . CurrentCompressedSize ) ;
CurrentMIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkSizeFName , Sizes . CurrentUncompressedSize ) ;
2024-06-17 16:41:27 -04:00
}
2024-06-11 16:46:57 -04:00
if ( BasePackage )
{
for ( const TPair < FIoChunkId , FIoHash > & ChunkHashPair : BasePackage - > ChunkHashes )
{
2024-06-17 16:41:27 -04:00
if ( ! ShouldProcessChunk ( ChunkHashPair . Key ) )
2024-06-11 16:46:57 -04:00
continue ;
const FIoHash * CurrentHash = nullptr ;
if ( CurrentPackage )
{
CurrentHash = CurrentPackage - > ChunkHashes . Find ( ChunkHashPair . Key ) ;
}
if ( CurrentHash = = nullptr )
{
2024-06-17 16:41:27 -04:00
PackagesWithDeletedChunks . Add ( IteratedPackage . Name ) ;
DeletedChunkPackagesByHash . FindOrAdd ( ChunkHashPair . Value ) . Add ( IteratedPackage . Name ) ;
2024-06-11 16:46:57 -04:00
continue ;
}
if ( * CurrentHash ! = ChunkHashPair . Value )
{
2024-06-17 16:41:27 -04:00
PackagesWithChangedChunks . Add ( IteratedPackage . Name ) ;
// All we can really do here is assume the entire package gets resent, which is not likely
// in the general case, but it _is_ reasonably likely in the cases where a package's bulk data changes,
// which happens to be what we select on.
// The counter argument is that it's possible that the bulk data is Very Large (i.e. multiple compression blocks), and only
// one block out of the entire thing changed.
if ( BaseMIAsset & & CurrentMIAsset )
{
2024-06-26 17:16:24 -04:00
TotalChangedSize + = Sizes . CurrentCompressedSize ;
2024-06-17 16:41:27 -04:00
}
2024-06-11 16:46:57 -04:00
}
}
}
if ( CurrentPackage )
{
for ( const TPair < FIoChunkId , FIoHash > & ChunkHashPair : CurrentPackage - > ChunkHashes )
{
2024-06-17 16:41:27 -04:00
if ( ! ShouldProcessChunk ( ChunkHashPair . Key ) )
{
2024-06-11 16:46:57 -04:00
continue ;
2024-06-17 16:41:27 -04:00
}
2024-06-11 16:46:57 -04:00
if ( ! BasePackage | |
BasePackage - > ChunkHashes . Contains ( ChunkHashPair . Key ) = = false )
{
2024-06-17 16:41:27 -04:00
PackagesWithNewChunks . FindOrAdd ( IteratedPackage . Name ) . Add ( ChunkHashPair . Value ) ;
2024-06-11 16:46:57 -04:00
}
}
}
}
2024-06-17 16:41:27 -04:00
TMap < FName , FName > MovedPackagesFromTo ;
// Look over the new packages - if any of them have exact matching entries in the deleted list,
// then we assume it's a moved chunk and remove it from the new/delete lists.
for ( const TPair < FName , TArray < FIoHash , TInlineAllocator < 1 > > > & PackageHashesPair : PackagesWithNewChunks )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
// Make sure all chunks we know about moved from the same place. We expect this to be only 1 for now, so warn on it.
FName MovedFrom = NAME_None ;
for ( const FIoHash & NewHash : PackageHashesPair . Value )
{
const TArray < FName , TInlineAllocator < 1 > > * PackagesThatHadThisChunk = DeletedChunkPackagesByHash . Find ( NewHash ) ;
if ( PackagesThatHadThisChunk = = nullptr | |
PackagesThatHadThisChunk - > Num ( ) = = 0 )
{
MovedFrom = NAME_None ;
break ;
}
// Due to duplication we could theoretically have the exact same bulk data in a bunch of
// different packages, so we consider it a move if it's in any of them. This could fail
// if there were multiple chunks where one came from one package and the other came from a different one,
// seems unlikely.
if ( MovedFrom . IsNone ( ) )
{
// Grab the first one...
MovedFrom = ( * PackagesThatHadThisChunk ) [ 0 ] ;
}
else
{
bool bFound = false ;
for ( const FName & PackageThatHadThisChunk : ( * PackagesThatHadThisChunk ) )
{
if ( MovedFrom = = PackageThatHadThisChunk )
{
bFound = true ;
break ;
}
}
if ( ! bFound )
{
MovedFrom = NAME_None ;
break ;
}
}
}
if ( MovedFrom . IsNone ( ) )
{
continue ; // Not moved - actual new package.
}
// We also only allow path moves - this is because it's not uncommon for folks to duplicate something like a mesh
// and change the material and this can confuse our hash matching.
// However, if it's a _Generated_ package we actually want to know because it might be an issue with the stability
// of the generator.
TStringBuilder < 64 > MovedFromStr ;
MovedFromStr < < MovedFrom ;
if ( ! FCString : : Stristr ( * MovedFromStr , TEXT ( " _GENERATED_ " ) ) )
{
// it's not generated, so make sure the name matches.
TStringBuilder < 64 > MovedToStr ;
MovedToStr < < PackageHashesPair . Key ;
const TCHAR * MovedFromShortName = FCString : : Strrchr ( * MovedFromStr , TEXT ( ' / ' ) ) ;
if ( MovedFromShortName )
{
MovedFromShortName + + ;
}
const TCHAR * MovedToShortName = FCString : : Strrchr ( * MovedToStr , TEXT ( ' / ' ) ) ;
if ( MovedToShortName )
{
MovedToShortName + + ;
}
// If we have short names and they are different, we assume it's not an actual move.
if ( MovedFromShortName & & MovedToShortName & & FCString : : Stricmp ( MovedFromShortName , MovedToShortName ) )
{
continue ;
}
}
if ( MovedPackagesFromTo . Contains ( MovedFrom ) )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Package %s appears to have moved twice. Perhaps duplicated multiple times and original deleted? Or Material change? " ) , * MovedFromStr ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Existing: %s " ) , * WriteToString < 64 > ( * MovedPackagesFromTo . Find ( MovedFrom ) ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " New: %s " ) , * WriteToString < 64 > ( PackageHashesPair . Key ) ) ;
continue ;
}
MovedPackagesFromTo . Add ( MovedFrom , PackageHashesPair . Key ) ;
2024-06-11 16:46:57 -04:00
}
2024-06-17 16:41:27 -04:00
// Done with this, empty it so it's obvious if we try to use it.
DeletedChunkPackagesByHash . Empty ( ) ;
// Once we have the list of moved packages, remove them from the deleted/new lists
for ( const TPair < FName , FName > & MovedPackageFromTo : MovedPackagesFromTo )
{
if ( ! PackagesWithNewChunks . Remove ( MovedPackageFromTo . Value ) )
{
UE_LOG ( LogDiffAssetBulk , Warning , TEXT ( " Unable to remove moved package %s from the new list " ) , * WriteToString < 64 > ( MovedPackageFromTo . Value ) ) ;
}
if ( ! PackagesWithDeletedChunks . Remove ( MovedPackageFromTo . Key ) )
{
UE_LOG ( LogDiffAssetBulk , Warning , TEXT ( " Unable to remove moved package %s from the deleted list " ) , * WriteToString < 64 > ( MovedPackageFromTo . Key ) ) ;
}
}
2024-06-11 16:46:57 -04:00
//
// We know what bulk datas *packages* changed. Try and see if any of the assets in the package have
// diff blame tags for us to determine cause. _usually_ there's one asset per package, but it's definitely
// possible to have more. Additionally _usually_ there's a good single candidate for assigning the data
// cost, however it is possible to have e.g. an importer create a lot of assets in a single package that
// all add bulk data to the package.
//
// Once we have FDerivedData we might be able to keep what data belongs to which asset.
//
struct FDiffResult
{
FString ChangedAssetObjectPath ;
FString TagBaseValue ;
FString TagCurrentValue ;
} ;
TMap < FName /* TagName */ , TMap < FTopLevelAssetPath /* AssetClass */ , TArray < FDiffResult > > > Results ;
TMap < FTopLevelAssetPath /* AssetClass */ , TArray < FName /* PackageName */ > > NoTagPackagesByAssumedClass ;
TArray < FName /* PackageName */ > PackagesWithUnassignableDiffsAndUntaggedAssets ;
TMap < FTopLevelAssetPath /* AssetClass */ , TArray < FName /* PackageName */ > > PackagesWithUnassignableDiffsByAssumedClass ;
2024-06-17 16:41:27 -04:00
for ( const FName & ChangedPackageName : PackagesWithChangedChunks )
2024-06-11 16:46:57 -04:00
{
2024-07-27 15:28:25 -04:00
TArray < FAssetData const * > BaseAssetDatas = BaseState . CopyAssetsByPackageName ( ChangedPackageName ) ;
TArray < FAssetData const * > CurrentAssetDatas = CurrentState . CopyAssetsByPackageName ( ChangedPackageName ) ;
2024-06-11 16:46:57 -04:00
struct FDiffTag
{
// Order is used to sort the diff blame keys so that the correct thing is blamed. This is
// so that e.g. changing the texture source (which would change the ddc key) gets properly blamed
// as it is lower order.
int Order ;
FName TagName ;
FString BaseValue ;
FString CurrentValue ;
const FAssetData * BaseAssetData ;
const FAssetData * CurrentAssetData ;
} ;
// We want to find all the tags that are in both base/current.
TMap < FName /* AssetName */ , TArray < FDiffTag > > PackageDiffTags ;
bool bPackageHasUntaggedAsset = false ;
for ( const FAssetData * BaseAssetData : BaseAssetDatas )
{
BaseAssetData - > EnumerateTags ( [ & PackageDiffTags , BaseAssetData , CurrentAssetDatas ] ( TPair < FName , FAssetTagValueRef > TagAndValue )
{
TCHAR Name [ NAME_SIZE ] ;
TagAndValue . Key . GetPlainNameString ( Name ) ;
if ( FCString : : Strncmp ( Name , TEXT ( " Cook_Diff_ " ) , 10 ) )
{
return ;
}
// This is O(N) but like 99.9% of the time there's only 1 asset.
const FAssetData * const * CurrentAssetData = CurrentAssetDatas . FindByPredicate ( [ SearchAssetName = & BaseAssetData - > AssetName ] ( const FAssetData * AssetData ) { return ( AssetData - > AssetName = = * SearchAssetName ) ; } ) ;
if ( CurrentAssetData = = nullptr )
{
return ;
}
FString CurrentValue ;
if ( CurrentAssetData [ 0 ] - > GetTagValue ( TagAndValue . Key , CurrentValue ) = = false )
{
// Both version don't have the tag so we can't compare.
return ;
}
TArray < FDiffTag > & AssetDiffTags = PackageDiffTags . FindOrAdd ( BaseAssetData - > AssetName ) ;
FDiffTag & Tag = AssetDiffTags . AddDefaulted_GetRef ( ) ;
Tag . Order = FCString : : Atoi ( Name + FCString : : Strlen ( TEXT ( " Cook_Diff_ " ) ) ) ; // this gets optimized to +10
Tag . TagName = TagAndValue . Key ;
Tag . BaseValue = TagAndValue . Value . AsString ( ) ;
Tag . CurrentValue = MoveTemp ( CurrentValue ) ;
Tag . BaseAssetData = BaseAssetData ;
Tag . CurrentAssetData = * CurrentAssetData ;
} ) ;
if ( PackageDiffTags . Contains ( BaseAssetData - > AssetName ) = = false )
{
bPackageHasUntaggedAsset = true ;
// An asset exists in the package that doesn't have any tags - make a note so that
// we can suggest this caused the bulk data diff if we don't find a blame.
}
}
bool bPackageHasUntaggedAndTaggedAssets = false ;
if ( PackageDiffTags . Num ( ) )
{
if ( bPackageHasUntaggedAsset )
{
bPackageHasUntaggedAndTaggedAssets = true ;
}
}
else
{
// Nothing has anything to use for diff blaming for this package.
// Try to find a representative asset class from the assets in the package.
FAssetData const * RepresentativeAsset = UE : : AssetRegistry : : GetMostImportantAsset ( CurrentAssetDatas , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : RequireOneTopLevelAsset ) ;
if ( RepresentativeAsset = = nullptr )
{
NoTagPackagesByAssumedClass . FindOrAdd ( FTopLevelAssetPath ( ) ) . Add ( ChangedPackageName ) ;
}
else
{
NoTagPackagesByAssumedClass . FindOrAdd ( RepresentativeAsset - > AssetClassPath ) . Add ( ChangedPackageName ) ;
}
continue ;
}
// Now we check and see if any of the diff tags can tell us why the package changed.
// We could find multiple assets that caused the change.
bool bFoundDiffTag = false ;
for ( TPair < FName , TArray < FDiffTag > > & AssetDiffTagPair : PackageDiffTags )
{
TArray < FDiffTag > & AssetDiffTags = AssetDiffTagPair . Value ;
Algo : : SortBy ( AssetDiffTags , & FDiffTag : : Order ) ;
for ( FDiffTag & Tag : AssetDiffTags )
{
if ( Tag . BaseValue ! = Tag . CurrentValue )
{
TMap < FTopLevelAssetPath , TArray < FDiffResult > > & TagResults = Results . FindOrAdd ( Tag . TagName ) ;
TArray < FDiffResult > & ClassResults = TagResults . FindOrAdd ( Tag . BaseAssetData - > AssetClassPath ) ;
FDiffResult & Result = ClassResults . AddDefaulted_GetRef ( ) ;
Result . ChangedAssetObjectPath = Tag . BaseAssetData - > GetObjectPathString ( ) ;
Result . TagBaseValue = MoveTemp ( Tag . BaseValue ) ;
Result . TagCurrentValue = MoveTemp ( Tag . CurrentValue ) ;
bFoundDiffTag = true ;
break ;
}
}
}
if ( bFoundDiffTag = = false )
{
// This means that all the tags they added didn't change, but the asset did.
// Assuming that a DDC key tag has been added, this means either:
//
// A) The asset changed independent of DDC key, which is a build consistency / determinism alert.
// B) The package had an asset with tags and an asset without tags, and the asset without tags caused
// the bulk data change.
//
// Unfortunately A) is a Big Deal and needs a warning, but B might end up being common due to blueprint classes,
// so we segregate the lists.
if ( bPackageHasUntaggedAndTaggedAssets )
{
PackagesWithUnassignableDiffsAndUntaggedAssets . Add ( ChangedPackageName ) ;
}
else
{
FAssetData const * RepresentativeAsset = UE : : AssetRegistry : : GetMostImportantAsset ( CurrentAssetDatas , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : RequireOneTopLevelAsset ) ;
if ( RepresentativeAsset = = nullptr )
{
PackagesWithUnassignableDiffsByAssumedClass . FindOrAdd ( FTopLevelAssetPath ( ) ) . Add ( ChangedPackageName ) ;
}
else
{
PackagesWithUnassignableDiffsByAssumedClass . FindOrAdd ( RepresentativeAsset - > AssetClassPath ) . Add ( ChangedPackageName ) ;
}
}
}
}
2024-06-17 16:41:27 -04:00
auto ProcessPackageClassAndSize = [ ] ( FAssetRegistryState & State , const FName & PackageName , uint64 & SizeToUpdate , TMap < FTopLevelAssetPath , TArray < FName > > & PackagesByClassToUpdate )
2024-06-11 16:46:57 -04:00
{
2024-07-27 15:28:25 -04:00
const FAssetData * MIAsset = UE : : AssetRegistry : : GetMostImportantAsset ( State . CopyAssetsByPackageName ( PackageName ) , UE : : AssetRegistry : : EGetMostImportantAssetFlags : : IgnoreSkipClasses ) ;
2024-06-17 16:41:27 -04:00
if ( MIAsset )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
// IoStoreUtilities puts the size of the package on the most important asset
uint64 CurrentCompressedSize = 0 ;
if ( MIAsset - > GetTagValue ( UE : : AssetRegistry : : Stage_ChunkCompressedSizeFName , CurrentCompressedSize ) )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
SizeToUpdate + = CurrentCompressedSize ;
2024-06-11 16:46:57 -04:00
}
2024-06-17 16:41:27 -04:00
PackagesByClassToUpdate . FindOrAdd ( MIAsset - > AssetClassPath ) . Add ( PackageName ) ;
}
} ;
auto SumPackageSizes = [ & PackageSizes ] ( const TArray < FName > & PackageList , bool bUseBaseSize )
{
uint64 Total = 0 ;
for ( const FName & PackageName : PackageList )
2024-06-11 16:46:57 -04:00
{
2024-06-26 17:16:24 -04:00
FPackageSizes * Sizes = PackageSizes . Find ( PackageName ) ;
2024-06-11 16:46:57 -04:00
if ( Sizes )
{
2024-06-26 17:16:24 -04:00
Total + = bUseBaseSize ? Sizes - > BaseCompressedSize : Sizes - > CurrentCompressedSize ;
2024-06-11 16:46:57 -04:00
}
}
2024-06-17 16:41:27 -04:00
return Total ;
} ;
2024-06-11 16:46:57 -04:00
2024-06-17 16:41:27 -04:00
TMap < FTopLevelAssetPath , TArray < FName > > NewPackagesByClass ;
uint64 TotalNewPackagesSize = 0 ;
for ( const TPair < FName , TArray < FIoHash , TInlineAllocator < 1 > > > & PackageHashesPair : PackagesWithNewChunks )
{
ProcessPackageClassAndSize ( CurrentState , PackageHashesPair . Key , TotalNewPackagesSize , NewPackagesByClass ) ;
2024-06-11 16:46:57 -04:00
}
2024-06-17 16:41:27 -04:00
TMap < FTopLevelAssetPath , TArray < FName > > DeletedPackagesByClass ;
uint64 TotalDeletedPackagesSize = 0 ;
for ( const FName & DeletedPackage : PackagesWithDeletedChunks )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
ProcessPackageClassAndSize ( BaseState , DeletedPackage , TotalDeletedPackagesSize , DeletedPackagesByClass ) ;
}
2024-06-11 16:46:57 -04:00
2024-06-17 16:41:27 -04:00
TMap < FTopLevelAssetPath , TArray < FName > > MovedPackagesByClass ;
uint64 TotalMovedPackagesSize = 0 ;
for ( const TPair < FName , FName > & MovedPackageFromTo : MovedPackagesFromTo )
{
ProcessPackageClassAndSize ( BaseState , MovedPackageFromTo . Key , TotalMovedPackagesSize , MovedPackagesByClass ) ;
}
int32 PackagesWithNoSize = UnionedPackages . Num ( ) - PackageSizes . Num ( ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " ===================================================== " ) ) ;
if ( bEvaluateOptional )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " OPTIONAL bulk data only " ) ) ;
}
else
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Excluding OPTIONAL bulk data chunks " ) ) ;
}
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Base Packages: %8d %17s bytes " ) , BasePackages . Num ( ) , * FText : : AsNumber ( BaseTotalSize ) . ToString ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Current Packages: %8d %17s bytes " ) , CurrentPackages . Num ( ) , * FText : : AsNumber ( CurrentTotalSize ) . ToString ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Bulk Data Packages Added: %8d %17s bytes " ) , PackagesWithNewChunks . Num ( ) , * FText : : AsNumber ( TotalNewPackagesSize ) . ToString ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Bulk Data Packages Deleted: %8d %17s bytes " ) , PackagesWithDeletedChunks . Num ( ) , * FText : : AsNumber ( TotalDeletedPackagesSize ) . ToString ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Bulk Data Packages Moved: %8d %17s bytes " ) , MovedPackagesFromTo . Num ( ) , * FText : : AsNumber ( TotalMovedPackagesSize ) . ToString ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Bulk Data Packages Changed: %8d %17s bytes (all chunks!) " ) , PackagesWithChangedChunks . Num ( ) , * FText : : AsNumber ( TotalChangedSize ) . ToString ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Packages with no size info: %8d " ) , PackagesWithNoSize ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
if ( PackagesWithChangedChunks . Num ( ) )
{
TArray < FName > & CantDetermineAssetClassPackages = NoTagPackagesByAssumedClass . FindOrAdd ( FTopLevelAssetPath ( ) ) ;
// Note this output is parsed by build scripts, be sure to fix those up if you change anything here.
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Changed package breakdown: // -ListNoBlame=<class name> " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " No blame information available: " ) ) ;
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
Algo : : Sort ( CantDetermineAssetClassPackages , FNameLexicalLess ( ) ) ;
2024-06-17 18:06:32 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Unknown %6d // Couldn't pick a representative asset in the package. -ListUnrepresented " ) , CantDetermineAssetClassPackages . Num ( ) ) ;
2024-06-17 16:41:27 -04:00
if ( bListUnrepresented )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
for ( const FName & PackageName : CantDetermineAssetClassPackages )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %s " ) , * PackageName . ToString ( ) ) ;
2024-06-11 16:46:57 -04:00
}
}
2024-06-17 16:41:27 -04:00
if ( ChangedCSVAr . IsValid ( ) )
{
for ( const FName & PackageName : CantDetermineAssetClassPackages )
{
ChangedCSVAr - > Logf ( TEXT ( " NoBlameInfo, Unknown, %s,, " ) , * WriteToString < 64 > ( PackageName ) ) ;
}
}
}
for ( TPair < FTopLevelAssetPath , TArray < FName > > & ClassPackages : NoTagPackagesByAssumedClass )
{
if ( ClassPackages . Key = = FTopLevelAssetPath ( ) ) // Skip packages we couldn't find a class for, handled above.
{
continue ;
}
uint64 TotalSizes = SumPackageSizes ( ClassPackages . Value , false ) ;
2024-06-11 16:46:57 -04:00
TStringBuilder < 64 > ClassName ;
ClassName < < ClassPackages . Key ;
2024-06-17 16:41:27 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %-37s %6d %17s bytes " ) , * ClassName , ClassPackages . Value . Num ( ) , * FText : : AsNumber ( TotalSizes ) . ToString ( ) ) ;
if ( ListNoBlame . Compare ( TEXT ( " All " ) , ESearchCase : : IgnoreCase ) = = 0 | |
ListNoBlame . Compare ( ClassPackages . Key . ToString ( ) , ESearchCase : : IgnoreCase ) = = 0 )
2024-06-11 16:46:57 -04:00
{
for ( const FName & PackageName : ClassPackages . Value )
{
2024-06-17 16:41:27 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %s " ) , * WriteToString < 64 > ( PackageName ) ) ;
2024-06-11 16:46:57 -04:00
}
}
2024-06-17 16:41:27 -04:00
if ( ChangedCSVAr . IsValid ( ) )
2024-06-11 16:46:57 -04:00
{
for ( const FName & PackageName : ClassPackages . Value )
{
2024-06-26 17:16:24 -04:00
FPackageSizes * Sizes = PackageSizes . Find ( PackageName ) ;
ChangedCSVAr - > Logf ( TEXT ( " NoBlameInfo, %s, %s,,,%s,%s,%s,%s " ) , * ClassName , * WriteToString < 64 > ( PackageName ) ,
Sizes ? * WriteToString < 32 > ( Sizes - > BaseCompressedSize ) : TEXT ( " " ) ,
Sizes ? * WriteToString < 32 > ( Sizes - > CurrentCompressedSize ) : TEXT ( " " ) ,
Sizes ? * WriteToString < 32 > ( Sizes - > BaseUncompressedSize ) : TEXT ( " " ) ,
Sizes ? * WriteToString < 32 > ( Sizes - > CurrentUncompressedSize ) : TEXT ( " " ) ) ;
2024-06-11 16:46:57 -04:00
}
}
}
2024-06-17 16:41:27 -04:00
if ( PackagesWithUnassignableDiffsByAssumedClass . Num ( ) )
{
int32 TotalUnassignablePackages = 0 ;
for ( TPair < FTopLevelAssetPath , TArray < FName > > & ClassPackages : PackagesWithUnassignableDiffsByAssumedClass )
{
TotalUnassignablePackages + = ClassPackages . Value . Num ( ) ;
}
2024-06-11 16:46:57 -04:00
2024-06-17 16:41:27 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Can't determine blame: %6d // Assets had blame tags but all matched - check determinism! -ListDeterminism " ) , TotalUnassignablePackages ) ;
for ( TPair < FTopLevelAssetPath , TArray < FName > > & ClassPackages : PackagesWithUnassignableDiffsByAssumedClass )
{
uint64 TotalSizes = SumPackageSizes ( ClassPackages . Value , false ) ;
TStringBuilder < 64 > ClassName ;
ClassName < < ClassPackages . Key ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %-37s %6d %17s bytes " ) , * ClassName , ClassPackages . Value . Num ( ) , * FText : : AsNumber ( TotalSizes ) . ToString ( ) ) ;
Algo : : Sort ( ClassPackages . Value , FNameLexicalLess ( ) ) ;
if ( bListDeterminism )
{
for ( const FName & PackageName : ClassPackages . Value )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %s " ) , * WriteToString < 64 > ( PackageName ) ) ;
}
}
if ( ChangedCSVAr . IsValid ( ) )
{
for ( const FName & PackageName : ClassPackages . Value )
{
ChangedCSVAr - > Logf ( TEXT ( " NonDetermistic, %s, %s,, " ) , * ClassName , * WriteToString < 64 > ( PackageName ) ) ;
}
}
}
}
if ( PackagesWithUnassignableDiffsAndUntaggedAssets . Num ( ) )
{
Algo : : Sort ( PackagesWithUnassignableDiffsAndUntaggedAssets , FNameLexicalLess ( ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Potential untagged assets: %6d // Package had assets with blame tags that matched, but also untagged assets. Might be determinism! -ListMixed " ) , PackagesWithUnassignableDiffsAndUntaggedAssets . Num ( ) ) ;
if ( bListMixed )
{
for ( const FName & PackageName : PackagesWithUnassignableDiffsAndUntaggedAssets )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %s " ) , * PackageName . ToString ( ) ) ;
}
}
if ( ChangedCSVAr . IsValid ( ) )
{
for ( const FName & PackageName : PackagesWithUnassignableDiffsAndUntaggedAssets )
{
ChangedCSVAr - > Logf ( TEXT ( " Mixed, Unknown, %s,, " ) , * WriteToString < 64 > ( PackageName ) ) ;
}
}
}
if ( Results . Num ( ) )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Summary changes by blame tag: // -ListBlame=<BlameTag> " ) ) ;
for ( TPair < FName , TMap < FTopLevelAssetPath , TArray < FDiffResult > > > & TagResults : Results )
{
uint32 TagCount = 0 ;
for ( TPair < FTopLevelAssetPath , TArray < FDiffResult > > & ClassResults : TagResults . Value )
{
TagCount + = ClassResults . Value . Num ( ) ;
}
TStringBuilder < 32 > TagName ;
TagName < < TagResults . Key ;
const TCHAR * * TagHelp = BuiltinDiffTagHelpMap . Find ( TagResults . Key ) ;
if ( TagHelp ! = nullptr )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %-37s %6d // %s " ) , * TagName , TagCount , * TagHelp ) ;
}
else
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %-37s %6d " ) , * TagName , TagCount ) ;
}
bool bListing = FCString : : Stricmp ( * ListBlame , TEXT ( " All " ) ) = = 0 | |
FCString : : Stricmp ( * ListBlame , * TagName ) = = 0 ;
for ( TPair < FTopLevelAssetPath , TArray < FDiffResult > > & ClassResults : TagResults . Value )
{
Algo : : SortBy ( ClassResults . Value , & FDiffResult : : ChangedAssetObjectPath ) ;
if ( bListing )
{
for ( FDiffResult & Result : ClassResults . Value )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %s [%s -> %s] " ) , * Result . ChangedAssetObjectPath , * Result . TagBaseValue , * Result . TagCurrentValue ) ;
}
}
if ( ChangedCSVAr . IsValid ( ) )
{
for ( FDiffResult & Result : ClassResults . Value )
{
ChangedCSVAr - > Logf ( TEXT ( " %s, %s, %s, %s, %s " ) , * TagResults . Key . ToString ( ) , * ClassResults . Key . ToString ( ) , * WriteToString < 64 > ( Result . ChangedAssetObjectPath ) , * Result . TagBaseValue , * Result . TagCurrentValue ) ;
}
}
}
}
}
} // end changed packages
auto ProcessPackagesByClass = [ & SumPackageSizes ] ( const TMap < FTopLevelAssetPath , TArray < FName > > & PackagesByClass , FArchive * CSVArchive , const TMap < FName , FName > * PackageDestinationIfMoved , bool bUseBaseSizes )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
for ( const TPair < FTopLevelAssetPath , TArray < FName > > & PackagesForClass : PackagesByClass )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
TStringBuilder < 64 > ClassName ;
ClassName < < PackagesForClass . Key ;
uint64 TotalSize = SumPackageSizes ( PackagesForClass . Value , bUseBaseSizes ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " %-37s %6d %17s bytes " ) , * ClassName , PackagesForClass . Value . Num ( ) , * FText : : AsNumber ( TotalSize ) . ToString ( ) , * ClassName ) ;
if ( CSVArchive )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
for ( const FName & PackageName : PackagesForClass . Value )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
if ( PackageDestinationIfMoved )
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
CSVArchive - > Logf ( TEXT ( " %s, %s, %s " ) , * ClassName , * WriteToString < 64 > ( PackageName ) , * WriteToString < 64 > ( * PackageDestinationIfMoved - > Find ( PackageName ) ) ) ;
2024-06-11 16:46:57 -04:00
}
2024-06-17 16:41:27 -04:00
else
2024-06-11 16:46:57 -04:00
{
2024-06-17 16:41:27 -04:00
CSVArchive - > Logf ( TEXT ( " %s, %s " ) , * ClassName , * WriteToString < 64 > ( PackageName ) ) ;
2024-06-11 16:46:57 -04:00
}
}
}
}
2024-06-17 16:41:27 -04:00
} ;
if ( PackagesWithNewChunks . Num ( ) )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " New package breakdown: " ) ) ;
ProcessPackagesByClass ( NewPackagesByClass , NewCSVAr . Get ( ) , nullptr , false ) ;
2024-06-11 16:46:57 -04:00
}
2024-06-17 16:41:27 -04:00
if ( PackagesWithDeletedChunks . Num ( ) )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Deleted package breakdown: " ) ) ;
ProcessPackagesByClass ( DeletedPackagesByClass , DeletedCSVAr . Get ( ) , nullptr , true ) ;
}
if ( MovedPackagesFromTo . Num ( ) )
{
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " " ) ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Moved package breakdown: " ) ) ;
ProcessPackagesByClass ( MovedPackagesByClass , MovedCSVAr . Get ( ) , & MovedPackagesFromTo , true ) ;
}
2024-06-11 16:46:57 -04:00
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Done. " ) ) ;
return 0 ;
}
INT32_MAIN_INT32_ARGC_TCHAR_ARGV ( )
{
FTaskTagScope Scope ( ETaskTag : : EGameThread ) ;
// start up the main loop
GEngineLoop . PreInit ( ArgC , ArgV ) ;
double StartTime = FPlatformTime : : Seconds ( ) ;
int32 Result = RunDiffAssetBulkData ( ) ;
UE_LOG ( LogDiffAssetBulk , Display , TEXT ( " Logging.. " ) ) ;
GLog - > Flush ( ) ;
RequestEngineExit ( TEXT ( " DiffAssetBulkData Exiting " ) ) ;
FEngineLoop : : AppPreExit ( ) ;
FModuleManager : : Get ( ) . UnloadModulesAtShutdown ( ) ;
FEngineLoop : : AppExit ( ) ;
return Result ;
}