312 lines
12 KiB
C++
312 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ValidationUtils.h"
|
|
|
|
#include "CustomizableObjectCompilationUtility.h"
|
|
#include "CustomizableObjectInstanceUpdateUtility.h"
|
|
#include "RHIGlobals.h"
|
|
#include "ScopedLogSection.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "Commandlets/Commandlet.h"
|
|
#include "Interfaces/ITargetPlatform.h"
|
|
#include "Interfaces/ITargetPlatformManagerModule.h"
|
|
#include "Misc/CoreMisc.h"
|
|
#include "MuCO/CustomizableObject.h"
|
|
#include "MuCO/CustomizableObjectPrivate.h"
|
|
#include "MuCO/CustomizableObjectSystem.h"
|
|
#include "MuCOE/CustomizableObjectBenchmarkingUtils.h"
|
|
#include "MuR/Model.h"
|
|
#include "Serialization/MemoryWriter.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
|
|
void PrepareAssetRegistry()
|
|
{
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName);
|
|
UE_LOG(LogMutable,Display,TEXT("Searching all assets (this will take some time)..."));
|
|
|
|
const double AssetRegistrySearchStartSeconds = FPlatformTime::Seconds();
|
|
AssetRegistryModule.Get().SearchAllAssets(true /* bSynchronousSearch */);
|
|
const double AssetRegistrySearchEndSeconds = FPlatformTime::Seconds() - AssetRegistrySearchStartSeconds;
|
|
UE_LOG(LogMutable, Log, TEXT("(double) asset_registry_search_time_s : %f "), AssetRegistrySearchEndSeconds);
|
|
|
|
UE_LOG(LogMutable,Display,TEXT("Asset searching completed in \"%f\" seconds!"), AssetRegistrySearchEndSeconds);
|
|
}
|
|
|
|
|
|
void LogGlobalSettings()
|
|
{
|
|
// Mutable Settings
|
|
const int32 WorkingMemoryKB = UCustomizableObjectSystem::GetInstanceChecked()->GetWorkingMemory() ;
|
|
UE_LOG(LogMutable,Log, TEXT("(int) working_memory_bytes : %d"), WorkingMemoryKB*1024)
|
|
UE_LOG(LogMutable, Display, TEXT("The mutable updates will use as working memory the value of %d KB"), WorkingMemoryKB)
|
|
|
|
// Expand this when adding new controls from the .xml file
|
|
|
|
// RHI Settings
|
|
UE_LOG(LogMutable, Log, TEXT("(string) rhi_adapter_name : %s"), *GRHIAdapterName )
|
|
}
|
|
|
|
|
|
void Wait(const double ToWaitSeconds)
|
|
{
|
|
check (ToWaitSeconds > 0);
|
|
|
|
const double EndSeconds = FPlatformTime::Seconds() + ToWaitSeconds;
|
|
UE_LOG(LogMutable,Display,TEXT("Holding test execution for %f seconds."),ToWaitSeconds);
|
|
while (FPlatformTime::Seconds() < EndSeconds)
|
|
{
|
|
// Tick the engine
|
|
CommandletHelpers::TickEngine();
|
|
|
|
// Stop if exit was requested
|
|
if (IsEngineExitRequested())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogMutable,Display,TEXT("Resuming test execution."));
|
|
}
|
|
|
|
|
|
FCompilationOptions GetCompilationOptionsForBenchmarking (const UCustomizableObject& ReferenceCustomizableObject)
|
|
{
|
|
// Override some configurations that may have been changed by the user
|
|
FCompilationOptions CISCompilationOptions = ReferenceCustomizableObject.GetPrivate()->GetCompileOptions();
|
|
CISCompilationOptions.OptimizationLevel = CustomizableObjectBenchmarkingUtils::GetOptimizationLevelForBenchmarking();
|
|
CISCompilationOptions.TextureCompression = ECustomizableObjectTextureCompression::Fast; // Does not affect instance update speed but does compilation
|
|
return CISCompilationOptions;
|
|
}
|
|
|
|
|
|
TArray<FAssetData> FindAllAssetsAtPath(FName SearchPath, const UClass* TargetObjectClass)
|
|
{
|
|
TArray<FAssetData> FoundAssetData;
|
|
|
|
if (TargetObjectClass)
|
|
{
|
|
FARFilter Filter;
|
|
Filter.ClassPaths.Add(TargetObjectClass->GetClassPathName());
|
|
Filter.PackagePaths.Add( SearchPath);
|
|
Filter.bRecursivePaths = true;
|
|
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName);
|
|
|
|
// Ensure the AR module is ready to search for stuff
|
|
AssetRegistryModule.Get().SearchAllAssets(true);
|
|
|
|
UE_LOG(LogMutable, Display, TEXT("Searching for all %s objects to test at path : %s ."), *TargetObjectClass->GetName(), *SearchPath.ToString());
|
|
AssetRegistryModule.Get().GetAssets(Filter, FoundAssetData);
|
|
UE_LOG(LogMutable, Display, TEXT("Search of %s objects completed. Found %i objects."), *TargetObjectClass->GetName(), FoundAssetData.Num());
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogMutable, Error, TEXT("No objects can be retrieved using a null class."));
|
|
}
|
|
|
|
return FoundAssetData;
|
|
}
|
|
|
|
|
|
bool GetDiskCompilationArg(const FString& Params)
|
|
{
|
|
bool bUseDiskCompilation = false;
|
|
if (! FParse::Bool(*Params,TEXT("UseDiskCompilation="),bUseDiskCompilation))
|
|
{
|
|
UE_LOG(LogMutable, Display, TEXT("Disk compilation setting for the compilation of the CO not specified. Using default value : %hhd"), bUseDiskCompilation) ;
|
|
}
|
|
return bUseDiskCompilation;
|
|
}
|
|
|
|
|
|
uint32 GetTargetAmountOfInstances(const FString& Params)
|
|
{
|
|
// Get the amount of instances to generate if parameter was provided (it will get multiplied by the amount of states later so this is a minimun value)
|
|
uint32 InstancesToGenerate = 16;
|
|
if (!FParse::Value(*Params, TEXT("InstanceGenerationCount="),InstancesToGenerate))
|
|
{
|
|
UE_LOG(LogMutable, Display, TEXT("Instance generation count not specified. Using default value : %u"), InstancesToGenerate);
|
|
}
|
|
return InstancesToGenerate;
|
|
}
|
|
|
|
|
|
ITargetPlatform* GetCompilationPlatform(const FString& Params)
|
|
{
|
|
// Get the package name of the CO to test
|
|
FString TargetPlatformName = "";
|
|
if (!FParse::Value(*Params, TEXT("CompilationPlatformName="), TargetPlatformName))
|
|
{
|
|
UE_LOG(LogMutable, Error, TEXT("Failed to parse the target compilation platform. Have you even provided the argument?"))
|
|
return nullptr;
|
|
}
|
|
|
|
// Set the target platform in the context. For now it is the current platform.
|
|
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
|
|
check(TPM);
|
|
|
|
ITargetPlatform* TargetCompilationPlatform = nullptr;
|
|
const TArray<ITargetPlatform*> TPMPlatforms = TPM->GetTargetPlatforms();
|
|
for (ITargetPlatform* Platform : TPMPlatforms)
|
|
{
|
|
FString PlatformName = Platform->PlatformName();
|
|
if (PlatformName.Compare(TargetPlatformName) == 0)
|
|
{
|
|
// We have found the platform provided
|
|
TargetCompilationPlatform = Platform;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!TargetCompilationPlatform)
|
|
{
|
|
UE_LOG(LogMutable, Error, TEXT("Unable to relate the provided platform name (%s) with the available platforms in this machine."), *TargetPlatformName);
|
|
}
|
|
|
|
return TargetCompilationPlatform;
|
|
}
|
|
|
|
|
|
bool TestCustomizableObject(UCustomizableObject& InTargetCustomizableObject, const ITargetPlatform& TargetCompilationPlatform,
|
|
const uint32 InstancesToGenerate, const bool bUseDiskCompilation)
|
|
{
|
|
const FScopedLogSection ObjectSection (EMutableLogSection::Object, FName( InTargetCustomizableObject.GetPathName()));
|
|
|
|
// Keep a strong object pointer pointing at the CO to prevent it from being GCd during the test
|
|
const TStrongObjectPtr<UCustomizableObject> TargetCO = TStrongObjectPtr{&InTargetCustomizableObject};
|
|
|
|
// Compile the Customizable Object ------------------------------------------------------------------------------ //
|
|
bool bWasCoCompilationSuccessful = false;
|
|
{
|
|
LLM_SCOPE_BYNAME(TEXT("MutableValidation/Compile"));
|
|
// Override some configurations that may have been changed by the user
|
|
FCompilationOptions CompilationOptions = GetCompilationOptionsForBenchmarking(InTargetCustomizableObject);
|
|
|
|
// Set the target compilation platform based on what the caller wants
|
|
CompilationOptions.TargetPlatform = &TargetCompilationPlatform;
|
|
// Disk cache usage for compilation operation
|
|
CompilationOptions.bUseDiskCompilation = bUseDiskCompilation;
|
|
|
|
TSharedRef<FCustomizableObjectCompilationUtility> CompilationUtility = MakeShared<FCustomizableObjectCompilationUtility>();
|
|
bWasCoCompilationSuccessful = CompilationUtility->CompileCustomizableObject(InTargetCustomizableObject, true, &CompilationOptions);
|
|
}
|
|
// -------------------------------------------------------------------------------------------------------------- //
|
|
|
|
if (!bWasCoCompilationSuccessful)
|
|
{
|
|
UE_LOG(LogMutable, Error, TEXT("The compilation of the Customizable object was not successful : No instances will be generated."));
|
|
return false; // Validation failed
|
|
}
|
|
|
|
// GHet the total size of the streaming data of the model ---------------------------------------------- //
|
|
{
|
|
const TSharedPtr<const mu::FModel> MutableModel = InTargetCustomizableObject.GetPrivate()->GetModel();
|
|
check (MutableModel);
|
|
|
|
// Roms ---------------------- //
|
|
{
|
|
const int32 RomCount = MutableModel->GetRomCount();
|
|
int64 TotalRomSizeBytes = 0;
|
|
for (int32 RomIndex = 0; RomIndex < RomCount; RomIndex++)
|
|
{
|
|
const uint32 RomByteSize = MutableModel->GetRomSize(RomIndex);
|
|
TotalRomSizeBytes += RomByteSize;
|
|
}
|
|
|
|
// Print MTU parseable logs
|
|
UE_LOG(LogMutable, Log, TEXT("(int) model_rom_count : %d "), RomCount);
|
|
UE_LOG(LogMutable, Log, TEXT("(int) model_roms_size : %lld "), TotalRomSizeBytes);
|
|
}
|
|
|
|
// CO embedded data size ------ //
|
|
{
|
|
TArray<uint8> EmbeddedDataBytes{};
|
|
FMemoryWriter SerializationTarget{EmbeddedDataBytes, false};
|
|
|
|
InTargetCustomizableObject.GetPrivate()->SaveEmbeddedData(SerializationTarget);
|
|
const int64 COEmbeddedDataSizeBytes = EmbeddedDataBytes.Num();
|
|
|
|
UE_LOG(LogMutable, Log, TEXT("(int) co_embedded_data_bytes : %lld "), COEmbeddedDataSizeBytes);
|
|
}
|
|
}
|
|
|
|
// Skip instances updating if no instances should be updated
|
|
if (InstancesToGenerate <= 0)
|
|
{
|
|
UE_LOG(LogMutable, Display, TEXT("Instances to generate are 0 : No instances will be generated."));
|
|
return true; // No instances are targeted for generation, this will be taken as compilation only test.
|
|
}
|
|
|
|
// Do not generate instances if the selected platform is not the running platform
|
|
if (&TargetCompilationPlatform != GetTargetPlatformManagerRef().GetRunningTargetPlatform())
|
|
{
|
|
UE_LOG(LogMutable, Display, TEXT("RunningPlatform != UserProvidedCompilationPlatform : No instances will be generated."));
|
|
return true;
|
|
}
|
|
|
|
// At this point we know the compilation has been successful. Generate a deterministically random set of instances now.
|
|
|
|
// Generate target random instances to be tested ------------------------------------------------------------ //
|
|
bool bWasInstancesCreationSuccessful = true;
|
|
TSpscQueue<TStrongObjectPtr<UCustomizableObjectInstance>> InstancesToProcess;
|
|
uint32 GeneratedInstances = 0;
|
|
{
|
|
LLM_SCOPE_BYNAME(TEXT("MutableValidation/GenerateInstances"));
|
|
|
|
// Create a set of instances so we can later test them out
|
|
bWasInstancesCreationSuccessful = CustomizableObjectBenchmarkingUtils::GenerateDeterministicSetOfInstances(InTargetCustomizableObject, InstancesToGenerate, InstancesToProcess, GeneratedInstances);
|
|
}
|
|
// ---------------------------------------------------------------------------------------------------------- //
|
|
|
|
UE_LOG(LogMutable, Log, TEXT("(int) generated_instances_count : %u "), GeneratedInstances);
|
|
|
|
// Update the instances generated --------------------------------------------------------------------------- //
|
|
UE_LOG(LogMutable, Display, TEXT("Updating generated instances..."));
|
|
bool bInstanceFailedUpdate = false;
|
|
const double InstancesUpdateStartSeconds = FPlatformTime::Seconds();
|
|
{
|
|
LLM_SCOPE_BYNAME(TEXT("MutableValidation/Update"));
|
|
|
|
TSharedRef<FCustomizableObjectInstanceUpdateUtility> InstanceUpdatingUtility = MakeShared<FCustomizableObjectInstanceUpdateUtility>();
|
|
|
|
TStrongObjectPtr<UCustomizableObjectInstance> InstanceToUpdate;
|
|
while (InstancesToProcess.Dequeue(InstanceToUpdate))
|
|
{
|
|
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, true);
|
|
check(InstanceToUpdate);
|
|
|
|
if (!InstanceUpdatingUtility->UpdateInstance(*InstanceToUpdate))
|
|
{
|
|
bInstanceFailedUpdate = true;
|
|
}
|
|
}
|
|
}
|
|
const double InstancesUpdateEndSeconds = FPlatformTime::Seconds();
|
|
|
|
// Notify and log time required by the instances to get updated
|
|
const double CombinedInstanceUpdateSeconds = InstancesUpdateEndSeconds - InstancesUpdateStartSeconds;
|
|
UE_LOG(LogMutable, Log, TEXT("(double) combined_update_time_ms : %f "), CombinedInstanceUpdateSeconds * 1000);
|
|
|
|
check(GeneratedInstances > 0);
|
|
const double AverageInstanceUpdateSeconds = CombinedInstanceUpdateSeconds / GeneratedInstances;
|
|
UE_LOG(LogMutable, Log, TEXT("(double) avg_update_time_ms : %f "), AverageInstanceUpdateSeconds * 1000);
|
|
|
|
UE_LOG(LogMutable, Display, TEXT("Generation of Customizable object instances took %f seconds (%f seconds avg)."), CombinedInstanceUpdateSeconds, AverageInstanceUpdateSeconds);
|
|
// ---------------------------------------------------------------------------------------------------------- //
|
|
|
|
// Compute instance update result
|
|
const bool bInstancesTestedSuccessfully = !bInstanceFailedUpdate && bWasInstancesCreationSuccessful;
|
|
if (bInstancesTestedSuccessfully)
|
|
{
|
|
UE_LOG(LogMutable, Display, TEXT("Generation of Customizable object instances was successful."));
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogMutable, Error, TEXT("The generation of Customizable object instances was not successful."));
|
|
}
|
|
|
|
return bInstancesTestedSuccessfully;
|
|
}
|
|
|
|
|