// Copyright Epic Games, Inc. All Rights Reserved. #include "CheckAndroidDeviceProfileCommandlet.h" #include "Containers/Array.h" #include "Containers/Map.h" #include "DeviceProfiles/DeviceProfile.h" #include "DeviceProfiles/DeviceProfileManager.h" #include "DeviceProfiles/DeviceProfileMatching.h" #include "Dom/JsonObject.h" #include "GenericPlatform/GenericPlatformMemory.h" #include "HAL/FileManager.h" #include "HAL/PlatformCrt.h" #include "IDeviceProfileSelectorModule.h" #include "JsonObjectConverter.h" #include "Logging/LogCategory.h" #include "Logging/LogMacros.h" #include "Misc/AssertionMacros.h" #include "Misc/CString.h" #include "Misc/ConfigCacheIni.h" #include "Misc/FileHelper.h" #include "Modules/ModuleManager.h" #include "PIEPreviewDeviceSpecification.h" #include "Serialization/JsonReader.h" #include "Serialization/JsonSerializer.h" #include "Templates/SharedPointer.h" #include "Templates/Tuple.h" #include "Trace/Detail/Channel.h" #include "UObject/Class.h" #include "UObject/NameTypes.h" #include "UObject/UnrealNames.h" DEFINE_LOG_CATEGORY_STATIC(LogCheckAndroidDeviceProfile, Log, All); EPlatformMemorySizeBucket DetermineDeviceMemorySizeBucket(int32 TotalPhysicalMemoryGB) { EPlatformMemorySizeBucket Bucket = EPlatformMemorySizeBucket::Default; static int32 LargestMemoryGB = 0, LargerMemoryGB = 0, DefaultMemoryGB = 0, SmallerMemoryGB = 0, SmallestMemoryGB = 0, TiniestMemoryGB = 0; static bool bTriedPlatformBuckets = false; if (!bTriedPlatformBuckets) { bTriedPlatformBuckets = true; FConfigFile EngineConfigFile; if (FConfigCacheIni::LoadLocalIniFile(EngineConfigFile, TEXT("Engine"), true, TEXT("Android"))) { // get values for this platform from it's .ini EngineConfigFile.GetInt(TEXT("PlatformMemoryBuckets"), TEXT("LargestMemoryBucket_MinGB"), LargestMemoryGB); EngineConfigFile.GetInt(TEXT("PlatformMemoryBuckets"), TEXT("LargerMemoryBucket_MinGB"), LargerMemoryGB); EngineConfigFile.GetInt(TEXT("PlatformMemoryBuckets"), TEXT("DefaultMemoryBucket_MinGB"), DefaultMemoryGB); EngineConfigFile.GetInt(TEXT("PlatformMemoryBuckets"), TEXT("SmallerMemoryBucket_MinGB"), SmallerMemoryGB); EngineConfigFile.GetInt(TEXT("PlatformMemoryBuckets"), TEXT("SmallestMemoryBucket_MinGB"), SmallestMemoryGB); EngineConfigFile.GetInt(TEXT("PlatformMemoryBuckets"), TEXT("TiniestMemoryBucket_MinGB"), TiniestMemoryGB); } } // if at least Smaller is specified, we can set the Bucket if (SmallerMemoryGB > 0) { if (TotalPhysicalMemoryGB >= SmallerMemoryGB) { Bucket = EPlatformMemorySizeBucket::Smaller; } else if (TotalPhysicalMemoryGB >= SmallestMemoryGB) { Bucket = EPlatformMemorySizeBucket::Smallest; } else { Bucket = EPlatformMemorySizeBucket::Tiniest; } } if (DefaultMemoryGB > 0 && TotalPhysicalMemoryGB >= DefaultMemoryGB) { Bucket = EPlatformMemorySizeBucket::Default; } if (LargerMemoryGB > 0 && TotalPhysicalMemoryGB >= LargerMemoryGB) { Bucket = EPlatformMemorySizeBucket::Larger; } if (LargestMemoryGB > 0 && TotalPhysicalMemoryGB >= LargestMemoryGB) { Bucket = EPlatformMemorySizeBucket::Largest; } return Bucket; } int32 UCheckAndroidDeviceProfileCommandlet::Main(const FString& RawCommandLine) { TArray Tokens; TArray Switches; TMap Params; ParseCommandLine(*RawCommandLine, Tokens, Switches, Params); FString ParamDeviceSpecsFolder = Params.FindRef(TEXT("DeviceSpecsFolder")); FString ParamDeviceSpecsFile = Params.FindRef(TEXT("DeviceSpecsFile")); FString ForcedDeviceProfileName = Params.FindRef(TEXT("OverrideDP")); FString OutputFolder = Params.FindRef(TEXT("OutDir")); TArray DeviceSpecificationFileNames; if (!ParamDeviceSpecsFolder.IsEmpty()) { IFileManager::Get().FindFiles(DeviceSpecificationFileNames, *(ParamDeviceSpecsFolder / TEXT("*.json")), true, false); } else if (!ParamDeviceSpecsFile.IsEmpty()) { DeviceSpecificationFileNames.Add(ParamDeviceSpecsFile); } FName DeviceProfileSelectorModule("AndroidDeviceProfileSelector"); IDeviceProfileSelectorModule* AndroidDeviceProfileSelector = FModuleManager::LoadModulePtr(DeviceProfileSelectorModule); if (ensure(AndroidDeviceProfileSelector != nullptr)) { do { TMap DeviceParameters; FString DeviceName; FString OutputFilename; if (DeviceSpecificationFileNames.Num()) { FString DeviceSpecsFile = DeviceSpecificationFileNames.Pop(); int32 ExtensionPos; if (DeviceSpecsFile.FindLastChar(TEXT('.'), ExtensionPos)) { DeviceName = DeviceSpecsFile.Left(ExtensionPos); OutputFilename = DeviceName; } FString JsonLocation = ParamDeviceSpecsFolder / DeviceSpecsFile; AndroidDeviceProfileSelector->GetDeviceParametersFromJson(JsonLocation, DeviceParameters); } else { DeviceName = TEXT("[no name]"); DeviceParameters.Add(FName(TEXT("SRC_GPUFamily")), Params.FindRef(TEXT("GPUFamily"))); DeviceParameters.Add(FName(TEXT("SRC_GLVersion")), Params.FindRef(TEXT("GLVersion"))); DeviceParameters.Add(FName(TEXT("SRC_VulkanAvailable")), Params.FindRef(TEXT("VulkanAvailable"))); DeviceParameters.Add(FName(TEXT("SRC_VulkanVersion")), Params.FindRef(TEXT("VulkanVersion"))); DeviceParameters.Add(FName(TEXT("SRC_AndroidVersion")), Params.FindRef(TEXT("AndroidVersion"))); DeviceParameters.Add(FName(TEXT("SRC_DeviceMake")), Tokens.Num() == 2 ? Tokens[0] : Params.FindRef(TEXT("DeviceMake"))); DeviceParameters.Add(FName(TEXT("SRC_DeviceModel")), Tokens.Num() == 1 ? Tokens[0] : Tokens.Num() == 2 ? Tokens[1] : Params.FindRef(TEXT("DeviceModel"))); DeviceParameters.Add(FName(TEXT("SRC_DeviceBuildNumber")), Params.FindRef(TEXT("DeviceBuildNumber"))); DeviceParameters.Add(FName(TEXT("SRC_UsingHoudini")), Params.FindRef(TEXT("UsingHoudini"))); DeviceParameters.Add(FName(TEXT("SRC_Hardware")), Params.FindRef(TEXT("Hardware"))); DeviceParameters.Add(FName(TEXT("SRC_Chipset")), Params.FindRef(TEXT("Chipset"))); DeviceParameters.Add(FName(TEXT("SRC_TotalPhysicalGB")), Params.FindRef(TEXT("TotalPhysicalGB"))); DeviceParameters.Add(FName(TEXT("SRC_HMDSystemName")), TEXT("")); DeviceParameters.Add(FName(TEXT("SRC_SM5Available")), Params.FindRef(TEXT("SM5Available"))); DeviceParameters.Add(FName(TEXT("SRC_ResolutionX")), Params.FindRef(TEXT("ResolutionX"))); DeviceParameters.Add(FName(TEXT("SRC_ResolutionY")), Params.FindRef(TEXT("ResolutionY"))); DeviceParameters.Add(FName(TEXT("SRC_InsetsLeft")), Params.FindRef(TEXT("InsetsLeft"))); DeviceParameters.Add(FName(TEXT("SRC_InsetsTop")), Params.FindRef(TEXT("InsetsTop"))); DeviceParameters.Add(FName(TEXT("SRC_InsetsRight")), Params.FindRef(TEXT("InsetsRight"))); DeviceParameters.Add(FName(TEXT("SRC_InsetsBottom")), Params.FindRef(TEXT("InsetsBottom"))); } AndroidDeviceProfileSelector->SetSelectorProperties(DeviceParameters); FString ProfileName = ForcedDeviceProfileName.IsEmpty() ? AndroidDeviceProfileSelector->GetDeviceProfileName() : ForcedDeviceProfileName; TArray SelectedFragments; UE_LOG(LogCheckAndroidDeviceProfile, Display, TEXT("%s Selected Device Profile: %s"), *DeviceName, *ProfileName); int32 TotalPhysGB = FCString::Atoi(*DeviceParameters.FindChecked(TEXT("SRC_TotalPhysicalGB"))); EPlatformMemorySizeBucket MemorySizeBucket = DetermineDeviceMemorySizeBucket(TotalPhysGB); FString PlatformOverride(TEXT("Android")); FString ProfileDescription; UDeviceProfile* Profile = UDeviceProfileManager::Get().FindProfile(ProfileName, false); TArray CVars; if (!Profile) { UE_LOG(LogCheckAndroidDeviceProfile, Error, TEXT("Failed to find requested device profile. %s requested Device Profile: %s But could not be found!"), *DeviceName, *ProfileName); } else { // Set the memory size bucket for this device. Profile->SetPreviewMemorySizeBucket(MemorySizeBucket); // ensure any previous expanded cvars are cleared out. Profile->ClearAllExpandedCVars(); // get all GetAllExpandedCVars re-runs the DP selection. TMap CVarMap = Profile->GetAllExpandedCVars(); for (auto CVarIt : CVarMap) { CVars.Add(FString::Printf(TEXT("%s=%s"), *CVarIt.Key, *CVarIt.Value)); } SelectedFragments = Profile->SelectedFragments; } if (!OutputFilename.IsEmpty()) { FString output; if (!DeviceName.IsEmpty()) { output += FString::Printf(TEXT("Device name : %s\n\n"), *DeviceName); } if (!Profile) { output += FString::Printf(TEXT("Failed to find device profile : %s\n\n"), *ProfileName); } OutputFilename = DeviceName + TEXT("_") + DeviceParameters.FindChecked(TEXT("SRC_GPUFamily")) + TEXT("_") + DeviceParameters.FindChecked(TEXT("SRC_TotalPhysicalGB")) + TEXT("GB.txt"); output += FString::Printf(TEXT("AndroidDeviceProfileSelector Params:\n")); TArray SortedKeys; DeviceParameters.GenerateKeyArray(SortedKeys); //SortedKeys.Sort(); for (FName& ParamKey : SortedKeys) { output += FString::Printf(TEXT("[%s]=%s\n"), *ParamKey.ToString(), *DeviceParameters.FindChecked(ParamKey)); } output += FString::Printf(TEXT("\nDevice Mem Bucket: %s\n"), LexToString(MemorySizeBucket)); output += FString::Printf(TEXT("\nSelected fragments :\n%s"), SelectedFragments.Num() == 0 ? TEXT("-none-\n") : TEXT("")); for (FSelectedFragmentProperties& Fragment : SelectedFragments) { FString FragTag; if (Fragment.Tag != NAME_None) { FragTag = TEXT("[") + Fragment.Tag.ToString() + TEXT("]"); } output += FString::Printf(TEXT("%s%s : Enabled=%s\n"), *FragTag, *Fragment.Fragment, Fragment.bEnabled ? TEXT("true") : TEXT("false")); } output += FString::Printf(TEXT("\n\nSelected device Profile: %s\nSelected device profile description: %s\n"), *ProfileName, *ProfileDescription); // may not want this: const bool bSortCVars = true; if (bSortCVars) { output += FString::Printf(TEXT("Sorted ")); CVars.Sort(); } FString CVarsOnly = FString::Printf(TEXT("CVars:\n")); for (FString& CVar : CVars) { CVarsOnly += FString::Printf(TEXT("%s\n"), *CVar); } output += CVarsOnly; FFileHelper::SaveStringToFile(output, *(OutputFolder / OutputFilename)); FFileHelper::SaveStringToFile(CVarsOnly, *(OutputFolder / TEXT("CVarsOnly") / OutputFilename)); } } while (DeviceSpecificationFileNames.Num()); } return 0; }