1792 lines
58 KiB
C++
1792 lines
58 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "Misc/App.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/CommandLine.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/OutputDeviceRedirector.h"
|
|
#include "ShaderCompilerCore.h"
|
|
#include "Stats/Stats.h"
|
|
#include "Misc/ScopedSlowTask.h"
|
|
#include "Misc/MonitoredProcess.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Interfaces/ITargetPlatform.h"
|
|
#include "Interfaces/ITargetPlatformControls.h"
|
|
#include "Interfaces/ITargetPlatformSettings.h"
|
|
#include "Interfaces/ITargetPlatformModule.h"
|
|
#include "Interfaces/ITargetPlatformControlsModule.h"
|
|
#include "Interfaces/ITargetPlatformSettingsModule.h"
|
|
#include "Interfaces/ITargetPlatformManagerModule.h"
|
|
#include "Interfaces/IAudioFormat.h"
|
|
#include "Interfaces/IAudioFormatModule.h"
|
|
#include "Interfaces/IShaderFormat.h"
|
|
#include "Interfaces/IShaderFormatModule.h"
|
|
#include "Interfaces/ITextureFormat.h"
|
|
#include "Interfaces/ITextureFormatManagerModule.h"
|
|
#include "Interfaces/ITextureFormatModule.h"
|
|
#include "Logging/StructuredLog.h"
|
|
#include "PlatformInfo.h"
|
|
#include "DesktopPlatformModule.h"
|
|
#include "Interfaces/ITurnkeySupportModule.h"
|
|
#include "DataDrivenShaderPlatformInfo.h"
|
|
#include "Serialization/JsonReader.h"
|
|
#include "Serialization/JsonSerializer.h"
|
|
#include "Misc/FileHelper.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogTargetPlatformManager, Log, All);
|
|
|
|
// AutoSDKs needs the extra DDPI info
|
|
#ifndef AUTOSDKS_ENABLED
|
|
#define AUTOSDKS_ENABLED DDPI_HAS_EXTENDED_PLATFORMINFO_DATA
|
|
#endif
|
|
|
|
|
|
|
|
#if AUTOSDKS_ENABLED
|
|
namespace UE::AutoSDK
|
|
{
|
|
static const TCHAR* SDKRootEnvVar = TEXT("UE_SDKS_ROOT");
|
|
static const TCHAR* SDKInstallManifestFileName = TEXT("CurrentlyInstalled.txt");
|
|
static const TCHAR* SDKLastScriptRunVersionFileName = TEXT("CurrentlyInstalled.Version.txt");
|
|
static const TCHAR* SDKRequiredScriptVersionFileName = TEXT("Version.txt");
|
|
static const TCHAR* SDKEnvironmentVarsFile = TEXT("OutputEnvVars.txt");
|
|
|
|
static FProcHandle AutoSDKSetupUBTProc;
|
|
|
|
static FConfigFile& GetCachedInfoToSaveAfterUBT()
|
|
{
|
|
static FConfigFile CachedInfoToSaveAfterUBT;
|
|
return CachedInfoToSaveAfterUBT;
|
|
}
|
|
|
|
static bool IsAutoSDKsEnabled()
|
|
{
|
|
FString SDKPath = FPlatformMisc::GetEnvironmentVariable(SDKRootEnvVar);
|
|
|
|
// AutoSDKs only enabled if UE_SDKS_ROOT is set.
|
|
if (SDKPath.Len() != 0)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static FString GetProjectPathForUBT()
|
|
{
|
|
if (FPaths::IsProjectFilePathSet())
|
|
{
|
|
return FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath());
|
|
}
|
|
if (FApp::HasProjectName())
|
|
{
|
|
FString ProjectPath = FPaths::ProjectDir() / FApp::GetProjectName() + TEXT(".uproject");
|
|
if (FPaths::FileExists(ProjectPath))
|
|
{
|
|
return ProjectPath;
|
|
}
|
|
}
|
|
return FString();
|
|
}
|
|
|
|
// get a value from an SDK.json file
|
|
static FString GetSDKInfo(const TCHAR* Platform, const TCHAR* Key)
|
|
{
|
|
// 1. start with per-project json
|
|
// a. look in platform extension location, then non-platext location, for a file
|
|
// b. if key not found, look up the ParentSdkFile chain (which is relative paths)
|
|
// 2. if key still not found, go back to 1. but using engine-wide SDK.json file
|
|
|
|
auto MakeConfigFilename = [Platform](const FString& RootDir) -> FString
|
|
{
|
|
FString PlatformExtensionLocation = FString::Printf(TEXT("%s/Platforms/%s/Config/%s_SDK.json"), *RootDir, Platform, Platform);
|
|
if (FPaths::FileExists(PlatformExtensionLocation))
|
|
{
|
|
return PlatformExtensionLocation;
|
|
}
|
|
FString StandardLocation = FString::Printf(TEXT("%s/Config/%s/%s_SDK.json"), *RootDir, Platform, Platform);
|
|
if (FPaths::FileExists(StandardLocation))
|
|
{
|
|
return StandardLocation;
|
|
}
|
|
return TEXT("");
|
|
};
|
|
|
|
auto GetJsonObject = [](const FString& Filename) -> TSharedPtr<FJsonObject>
|
|
{
|
|
FString FileContents;
|
|
if (!FFileHelper::LoadFileToString(FileContents, *Filename))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> Object;
|
|
TSharedRef<TJsonReader<> > Reader = TJsonReaderFactory<>::Create(FileContents);
|
|
if (!FJsonSerializer::Deserialize(Reader, Object) || !Object.IsValid())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return Object;
|
|
};
|
|
|
|
// Steps 1 or 2 above
|
|
for (int Pass = 0; Pass < 2; Pass++)
|
|
{
|
|
// Step 1 - ProjectDir; Step 2 - EngineDir
|
|
FString RootDir = Pass == 0 ? GetProjectPathForUBT() : FPaths::EngineDir();
|
|
// Step a. Choose platformextension or standard location
|
|
FString InfoFile = RootDir.IsEmpty() ? FString() : MakeConfigFilename(RootDir);
|
|
|
|
// make sure we found a file
|
|
if (InfoFile.IsEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FString ParentFile;
|
|
do
|
|
{
|
|
// read the file and get the key if it exists
|
|
TSharedPtr<FJsonObject> JsonObj = GetJsonObject(InfoFile);
|
|
if (JsonObj != nullptr)
|
|
{
|
|
if (JsonObj->HasField(Key))
|
|
{
|
|
// if we have a value, we are done!
|
|
return JsonObj->GetStringField(Key);
|
|
}
|
|
// step b. go up parent chain
|
|
if (JsonObj->HasField(TEXT("ParentSDKFile")))
|
|
{
|
|
ParentFile = JsonObj->GetStringField(TEXT("ParentSDKFile"));
|
|
|
|
// the parent field is relative to current json file
|
|
InfoFile = FPaths::GetPath(InfoFile) / ParentFile;
|
|
FPaths::NormalizeFilename(InfoFile);
|
|
}
|
|
else
|
|
{
|
|
ParentFile = TEXT("");
|
|
}
|
|
}
|
|
} while (!ParentFile.IsEmpty());
|
|
}
|
|
|
|
// if we got no value, then return empty string for failure
|
|
return FString();
|
|
}
|
|
|
|
static FString GetAutoSDKPlatformDir(const TCHAR* Platform)
|
|
{
|
|
FString SDKPath = FPlatformMisc::GetEnvironmentVariable(SDKRootEnvVar);
|
|
#if PLATFORM_WINDOWS
|
|
static FString HostPlatform(TEXT("HostWin64"));
|
|
#else
|
|
static FString HostPlatform = FString::Printf(TEXT("Host%hs"), FPlatformProperties::IniPlatformName());
|
|
#endif
|
|
FString AutoSDKPlatform = GetSDKInfo(Platform, TEXT("AutoSDKPlatform"));
|
|
|
|
return FPaths::Combine(*SDKPath, *HostPlatform, AutoSDKPlatform.Len() > 0 ? *AutoSDKPlatform : Platform);
|
|
}
|
|
|
|
|
|
// Check to see if any platforms are not up to date with AutoSDK setup. This is how it's determined:
|
|
// 1. Load an existing <proj>/Intermediate/CachedAutoSdkInfo.ini to get cached info from last run
|
|
// 2. -> Compare current vs cached value for UE_SDKS_ROOT
|
|
// 3. For each (real) platform:
|
|
// a. Get the current AutoSDKVersion from <platform>_SDK.json (looking in project overrides, and parent SDK.json chain as needed)
|
|
// b. -> Compare current vs cached AutoSDKVersion
|
|
// c. Set TargetSDKRoot to UE_SDKS_ROOT/<Platform>/<AutoSDKVersion>
|
|
// d. Check if TargetSDKRoot/AutoSDKVersion/setup.bat|sh [aka SetupScript] exists
|
|
// e. -> Compare current vs cached SetupScript existence
|
|
// f. Read TargetSDKRoot/CurrentlyInstalled.txt
|
|
// g. Get installed version, and if it's AutoSDK or ManualSDK
|
|
// h. If ManualSDK, skip and move to next platform
|
|
// i. -> Compare installed version to AutoSDKVersion
|
|
// j. -> Compare installed "script version" to required "script version" (this manages updating setup.bat)
|
|
// j. -> Check for existence of TargetSDKRoot/OutputEnvVars.txt
|
|
// 4. If anything above (marked with ->) doesn't match up, then run UBT which will update CurrentlyInstalled.txt and OutputEnvVars.txt
|
|
// 5. [much later] Read in OutputEnvVars.txt (either from step 4, or a previous UBT run) to set envvars for the editor process
|
|
//
|
|
// Still todo:
|
|
// Manage CurrentlyInstalled.Version.txt, which allows for forcing a re-run of the SetupScript, which would mean that we need to run UBT
|
|
// Double check what happens if a user goes from manual->auto or auto->manual
|
|
// We can't tell here if the platform prefers auto vs manual, which may affect the previous line
|
|
// ManualSDKEnvVars.txt? I believe this is not used and we can ignore it
|
|
// Maybe force run UBT if UBT.dll has been updated since last run of editor, just in case
|
|
static bool NeedsToRunSetupPlatformsUBT()
|
|
{
|
|
// early out and always return true on builders
|
|
if (GIsBuildMachine)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
SCOPED_BOOT_TIMING("AutoSDKInit - NeedsToRunSetupPlatformsUBT");
|
|
|
|
FString CachedAutoSDKPath = FPaths::ProjectIntermediateDir() / TEXT("CachedAutoSdkInfo.ini");
|
|
FConfigFile CachedInfo;
|
|
CachedInfo.Read(CachedAutoSDKPath);
|
|
|
|
// first check that the UE_SDKS_ROOT var is the same
|
|
FString SDKsRoot = FPlatformMisc::GetEnvironmentVariable(UE::AutoSDK::SDKRootEnvVar);
|
|
FString CachedValue;
|
|
CachedInfo.GetString(TEXT("Global"), TEXT("SDKsRoot"), CachedValue);
|
|
GetCachedInfoToSaveAfterUBT().SetString(TEXT("Global"), TEXT("SDKsRoot"), *SDKsRoot);
|
|
|
|
// even if we need to run, we finish this function to get all the cached info to check next run
|
|
bool bNeedsToRun = false;
|
|
if (SDKsRoot != CachedValue)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because the value of %s changed"), SDKRootEnvVar);
|
|
bNeedsToRun = true;
|
|
}
|
|
|
|
for (const TPair<FName, FDataDrivenPlatformInfo>& Pair : FDataDrivenPlatformInfoRegistry::GetAllPlatformInfos())
|
|
{
|
|
// check only real platforms, that have an AutoSDK path
|
|
if (!Pair.Value.bEnabledForUse || Pair.Value.bIsFakePlatform || Pair.Value.AutoSDKPath.IsEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// get the AutoSDK version - AutoSDKDirectory if specified, or use default of MainVersion
|
|
FString PlatformName = Pair.Key.ToString();
|
|
FString AutoSDKVersion = GetSDKInfo(*PlatformName, TEXT("AutoSDKDirectory"));
|
|
if (AutoSDKVersion.IsEmpty())
|
|
{
|
|
AutoSDKVersion = GetSDKInfo(*PlatformName, TEXT("MainVersion"));
|
|
}
|
|
CachedInfo.GetString(*PlatformName, TEXT("AutoSDKDirectory"), CachedValue);
|
|
GetCachedInfoToSaveAfterUBT().SetString(*PlatformName, TEXT("AutoSDKDirectory"), *AutoSDKVersion);
|
|
|
|
if (AutoSDKVersion != CachedValue)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because AutoSDK version for %s changed"), *PlatformName);
|
|
bNeedsToRun = true;
|
|
}
|
|
|
|
FString TargetSDKRoot = GetAutoSDKPlatformDir(*PlatformName);
|
|
|
|
// check if presence of setup.bat, etc matches
|
|
#if PLATFORM_WINDOWS
|
|
FString SetupScript = TargetSDKRoot / AutoSDKVersion / "setup.bat";
|
|
#else
|
|
FString SetupScript = TargetSDKRoot / AutoSDKVersion / "setup.sh";
|
|
#endif
|
|
bool bSetupScriptExists = FPaths::FileExists(SetupScript);
|
|
bool bCachedValue;
|
|
CachedInfo.GetBool(*PlatformName, TEXT("bSetupScriptExists"), bCachedValue);
|
|
GetCachedInfoToSaveAfterUBT().SetBool(*PlatformName, TEXT("bSetupScriptExists"), bSetupScriptExists);
|
|
if (bSetupScriptExists != bCachedValue)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because the existence of %s changed"), *SetupScript);
|
|
bNeedsToRun = true;
|
|
}
|
|
|
|
// only deal with autosdk stuff if the setupscript actually exists now - if it doesn't, none of this will do anything in UBT
|
|
if (bSetupScriptExists)
|
|
{
|
|
// check the currently installed manifest file - if the version doesn't match and it's not a ManualSDK installation,
|
|
// we will need to run UBT
|
|
TArray<FString> FileLines;
|
|
bool bIsManualInstall = false;
|
|
if (FFileHelper::LoadFileToStringArray(FileLines, *(TargetSDKRoot / SDKInstallManifestFileName)) &&
|
|
FileLines.Num() > 1)
|
|
{
|
|
if (FileLines[1] == TEXT("ManualSDK"))
|
|
{
|
|
bIsManualInstall = true;
|
|
}
|
|
if (FileLines[0] != AutoSDKVersion && !bIsManualInstall)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because the installed version of %s doesn't match the required version"), *PlatformName);
|
|
bNeedsToRun = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because the the file %s is invalid"), *(TargetSDKRoot / SDKInstallManifestFileName));
|
|
bNeedsToRun = true;
|
|
}
|
|
|
|
if (!bIsManualInstall)
|
|
{
|
|
// check if the last run script version matches what the AutoSDK version requires
|
|
// if they mismatch, or if the installed version is missing, run UBT
|
|
FString InstalledScriptVersion;
|
|
FString RequiredScriptVersion;
|
|
if (!FFileHelper::LoadFileToString(RequiredScriptVersion, *(TargetSDKRoot / AutoSDKVersion / SDKRequiredScriptVersionFileName)))
|
|
{
|
|
RequiredScriptVersion = TEXT("UnspecifiedScriptVersion");
|
|
}
|
|
if (!FFileHelper::LoadFileToString(InstalledScriptVersion, *(TargetSDKRoot / SDKLastScriptRunVersionFileName)) ||
|
|
RequiredScriptVersion.TrimStartAndEnd() != InstalledScriptVersion.TrimStartAndEnd())
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because the last run script version in %s doesn't match required (%s !- %s)"), *(TargetSDKRoot / SDKLastScriptRunVersionFileName), *InstalledScriptVersion, *RequiredScriptVersion);
|
|
bNeedsToRun = true;
|
|
}
|
|
}
|
|
|
|
// autosdk writes out the OUtputEnvvars.txt file that should exists if autosdk ran
|
|
if (!bIsManualInstall && !FPaths::FileExists(*(TargetSDKRoot / SDKEnvironmentVarsFile)))
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Running UBT for AutoSDK init because the file %s is missing"), *(TargetSDKRoot / SDKEnvironmentVarsFile));
|
|
bNeedsToRun = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
UE_LOGFMT(LogTargetPlatformManager, Log, "{CachedAutoSDKPath} is {Status}", CachedAutoSDKPath, bNeedsToRun ? "out-of-date" : "up-to-date");
|
|
return bNeedsToRun;
|
|
}
|
|
|
|
// kick off a call to UBT nice and early so that it's results are hopefully ready when needed
|
|
FDelayedAutoRegisterHelper GAutoSDKInit(EDelayedRegisterRunPhase::FileSystemReady, []
|
|
{
|
|
SCOPED_BOOT_TIMING("AutoSDKInit - DelayedAutoRegisterHelper");
|
|
|
|
// amortize UBT cost by calling it once for all platforms, rather than once per platform.
|
|
if (IsAutoSDKsEnabled())
|
|
{
|
|
FString UBTParams(TEXT("-Mode=ValidatePlatforms -AllPlatforms -OutputSDKs"));
|
|
{
|
|
if (FString Project = GetProjectPathForUBT(); Project.Len() > 0)
|
|
{
|
|
UBTParams += FString::Printf(TEXT(" -project=%s"), *Project);
|
|
}
|
|
// Write the output to a separate file since it performs much better than read pipes in this scenario
|
|
// where the invocation script is quite involved and is in turn calling other scripts and programs...
|
|
{
|
|
FString LogFile = FPaths::Combine(FPaths::ProjectLogDir(), "AutoSDKInfo.txt");
|
|
FString AbsLogFile = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*LogFile);
|
|
UBTParams += FString::Printf(TEXT(" -log=\"%s\" -verbose -timestamps"), *AbsLogFile);
|
|
}
|
|
}
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("Multiprocess")) == false && NeedsToRunSetupPlatformsUBT())
|
|
{
|
|
int32 UBTReturnCode = -1;
|
|
|
|
void* ReadPipe = nullptr;
|
|
void* WritePipe = nullptr;
|
|
AutoSDKSetupUBTProc = FDesktopPlatformModule::Get()->InvokeUnrealBuildToolAsync(UBTParams, *GLog, ReadPipe, WritePipe, true);
|
|
if (!AutoSDKSetupUBTProc.IsValid())
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Warning, TEXT("AutoSDK is enabled (UE_SDKS_ROOT is set), but failed to run UBT to check SDK status! Check your installation."));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOGFMT(LogTargetPlatformManager, Log, "Skip running UBT AutoSDK init with params [{UBTParams}]", UBTParams);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
static const size_t MaxPlatformCount = 64; // In the unlikely event that someone bumps this please note that there's
|
|
// an implicit assumption that there won't be more than 64 unique target
|
|
// platforms in the FTargetPlatformSet code since it uses one bit of an
|
|
// uint64 per platform.
|
|
|
|
static const ITargetPlatform* TargetPlatformArray[MaxPlatformCount];
|
|
static const ITargetPlatformControls* TargetPlatformControlsArray[MaxPlatformCount];
|
|
|
|
static int32 PlatformCounter = 0;
|
|
static int32 PlatformControlsCounter = 0;
|
|
|
|
int32 ITargetPlatform::AssignPlatformOrdinal(const ITargetPlatform& Platform)
|
|
{
|
|
check(PlatformCounter < MaxPlatformCount);
|
|
|
|
const int32 Ordinal = PlatformCounter++;
|
|
|
|
check(TargetPlatformArray[Ordinal] == nullptr);
|
|
TargetPlatformArray[Ordinal] = &Platform;
|
|
|
|
return Ordinal;
|
|
}
|
|
int32 ITargetPlatformControls::AssignPlatformOrdinal(const ITargetPlatformControls& Platform)
|
|
{
|
|
check(PlatformControlsCounter < MaxPlatformCount);
|
|
|
|
const int32 Ordinal = PlatformControlsCounter++;
|
|
|
|
check(TargetPlatformControlsArray[Ordinal] == nullptr);
|
|
TargetPlatformControlsArray[Ordinal] = &Platform;
|
|
|
|
return Ordinal;
|
|
}
|
|
|
|
const ITargetPlatformControls* ITargetPlatformControls::GetPlatformFromOrdinal(int32 Ordinal)
|
|
{
|
|
check(Ordinal < PlatformControlsCounter);
|
|
|
|
return TargetPlatformControlsArray[Ordinal];
|
|
}
|
|
|
|
const class ITargetPlatformSettings& ITargetDevice::GetPlatformSettings() const
|
|
{
|
|
return *(GetTargetPlatform().GetTargetPlatformSettings());
|
|
}
|
|
const class ITargetPlatformControls& ITargetDevice::GetPlatformControls() const
|
|
{
|
|
return *(GetTargetPlatform().GetTargetPlatformControls());
|
|
}
|
|
|
|
ITargetPlatform::FOnTargetDeviceDiscovered& ITargetPlatform::OnDeviceDiscovered()
|
|
{
|
|
static FOnTargetDeviceDiscovered Delegate;
|
|
return Delegate;
|
|
}
|
|
|
|
ITargetPlatform::FOnTargetDeviceLost& ITargetPlatform::OnDeviceLost()
|
|
{
|
|
static FOnTargetDeviceLost Delegate;
|
|
return Delegate;
|
|
}
|
|
|
|
ITargetPlatformControls::FOnTargetDeviceDiscovered& ITargetPlatformControls::OnDeviceDiscovered()
|
|
{
|
|
static FOnTargetDeviceDiscovered Delegate;
|
|
return Delegate;
|
|
}
|
|
|
|
ITargetPlatformControls::FOnTargetDeviceLost& ITargetPlatformControls::OnDeviceLost()
|
|
{
|
|
static FOnTargetDeviceLost Delegate;
|
|
return Delegate;
|
|
}
|
|
|
|
/**
|
|
* Module for the target platform manager
|
|
*/
|
|
class FTargetPlatformManagerModule
|
|
: public ITargetPlatformManagerModule
|
|
{
|
|
public:
|
|
|
|
/** Default constructor. */
|
|
FTargetPlatformManagerModule()
|
|
: bRestrictFormatsToRuntimeOnly(false)
|
|
, bForceCacheUpdate(true)
|
|
, bHasInitErrors(false)
|
|
, bIgnoreFirstDelegateCall(true)
|
|
{
|
|
#if WITH_EDITOR && UE_WITH_TURNKEY_SUPPORT
|
|
|
|
ITurnkeySupportModule::Get().UpdateSdkInfo();
|
|
#endif
|
|
|
|
#if AUTOSDKS_ENABLED
|
|
|
|
// AutoSDKs only enabled if UE_SDKS_ROOT is set.
|
|
if (UE::AutoSDK::IsAutoSDKsEnabled())
|
|
{
|
|
if (UE::AutoSDK::AutoSDKSetupUBTProc.IsValid())
|
|
{
|
|
SCOPED_BOOT_TIMING("AutoSDKInit - WaitForUBTProc");
|
|
// wait for UBT to finish
|
|
FPlatformProcess::WaitForProc(UE::AutoSDK::AutoSDKSetupUBTProc);
|
|
|
|
// if it failed, we don't want to cache the saved info, so we make sure we run it again next time
|
|
int32 ReturnCode = 0;
|
|
FPlatformProcess::GetProcReturnCode(UE::AutoSDK::AutoSDKSetupUBTProc, &ReturnCode);
|
|
FPlatformProcess::CloseProc(UE::AutoSDK::AutoSDKSetupUBTProc);
|
|
UE::AutoSDK::AutoSDKSetupUBTProc.Reset();
|
|
UE_LOGFMT(LogTargetPlatformManager, Log, "UBT AutoSDK ReturnCode: {ReturnCode}", ReturnCode);
|
|
|
|
if (ReturnCode == 0)
|
|
{
|
|
// now save out the cached info so next run we can skip UBT
|
|
UE::AutoSDK::GetCachedInfoToSaveAfterUBT().Write(*(FPaths::ProjectIntermediateDir() / TEXT("CachedAutoSdkInfo.ini")));
|
|
}
|
|
}
|
|
|
|
// we have to setup our local environment according to AutoSDKs or the ITargetPlatform's IsSDkInstalled calls may fail
|
|
// before we get a change to setup for a given platform. Use the platforminfo list to avoid any kind of interdependency.
|
|
SCOPED_BOOT_TIMING("AutoSDKInit - SetupAndValidateAutoSDK");
|
|
for (auto Pair: FDataDrivenPlatformInfoRegistry::GetAllPlatformInfos())
|
|
{
|
|
if (Pair.Value.AutoSDKPath.Len() > 0)
|
|
{
|
|
SetupAndValidateAutoSDK(Pair.Value.AutoSDKPath);
|
|
}
|
|
}
|
|
|
|
FString ManualSDKEnvironmentVarsPath = FPaths::EngineIntermediateDir() / FString(TEXT("ManualSDKEnvVars.txt"));
|
|
if (IFileManager::Get().FileExists(*ManualSDKEnvironmentVarsPath))
|
|
{
|
|
SetupEnvironmentFromManualSDK(ManualSDKEnvironmentVarsPath);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TextureFormatManager = FModuleManager::LoadModulePtr<ITextureFormatManagerModule>("TextureFormat");
|
|
|
|
// Calling a virtual function from a constructor, but with no expectation that a derived implementation of this
|
|
// method would be called. This is solely to avoid duplicating code in this implementation, not for polymorphism.
|
|
FTargetPlatformManagerModule::Invalidate();
|
|
|
|
FModuleManager::Get().OnModulesChanged().AddRaw(this, &FTargetPlatformManagerModule::ModulesChangesCallback);
|
|
}
|
|
|
|
/** Destructor. */
|
|
virtual ~FTargetPlatformManagerModule()
|
|
{
|
|
FModuleManager::Get().OnModulesChanged().RemoveAll(this);
|
|
}
|
|
|
|
public:
|
|
|
|
// ITargetPlatformManagerModule interface
|
|
|
|
virtual bool HasInitErrors(FString* OutErrorMessages) const
|
|
{
|
|
if (OutErrorMessages)
|
|
{
|
|
*OutErrorMessages = InitErrorMessages;
|
|
}
|
|
return bHasInitErrors;
|
|
}
|
|
|
|
virtual void Invalidate() override
|
|
{
|
|
bForceCacheUpdate = true;
|
|
|
|
SetupSDKStatus();
|
|
//GetTargetPlatforms(); redudant with next call
|
|
GetActiveTargetPlatforms();
|
|
|
|
bForceCacheUpdate = false;
|
|
|
|
// If we've had an error due to an invalid target platform, don't do additional work
|
|
if (!bHasInitErrors)
|
|
{
|
|
GetAudioFormats();
|
|
GetShaderFormats();
|
|
}
|
|
|
|
OnTargetPlatformsInvalidated.Broadcast();
|
|
}
|
|
|
|
virtual FOnTargetPlatformsInvalidated& GetOnTargetPlatformsInvalidatedDelegate() override
|
|
{
|
|
return OnTargetPlatformsInvalidated;
|
|
}
|
|
|
|
virtual const TArray<ITargetPlatform*>& GetTargetPlatforms() override
|
|
{
|
|
if (Platforms.Num() == 0 || bForceCacheUpdate)
|
|
{
|
|
DiscoverAvailablePlatforms();
|
|
}
|
|
|
|
return Platforms;
|
|
}
|
|
|
|
virtual const TArray<ITargetPlatformControls*>& GetTargetPlatformControls() override
|
|
{
|
|
if (PlatformControls.Num() == 0 || bForceCacheUpdate)
|
|
{
|
|
DiscoverAvailablePlatforms();
|
|
}
|
|
|
|
return PlatformControls;
|
|
}
|
|
|
|
virtual const TArray<ITargetPlatformSettings*>& GetTargetPlatformSettings() override
|
|
{
|
|
if (PlatformSettings.Num() == 0 || bForceCacheUpdate)
|
|
{
|
|
DiscoverAvailablePlatforms();
|
|
}
|
|
|
|
return PlatformSettings;
|
|
}
|
|
|
|
virtual ITargetDevicePtr FindTargetDevice(const FTargetDeviceId& DeviceId) override
|
|
{
|
|
ITargetPlatform* Platform = FindTargetPlatform(DeviceId.GetPlatformName());
|
|
|
|
if (Platform != nullptr)
|
|
{
|
|
return Platform->GetDevice(DeviceId);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
virtual ITargetPlatform* FindTargetPlatform(FStringView Name) override
|
|
{
|
|
GetTargetPlatforms(); // Populates PlatformsByName
|
|
|
|
if (ITargetPlatform** Platform = PlatformsByName.Find(FName(Name)))
|
|
{
|
|
return *Platform;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
virtual ITargetPlatform* FindTargetPlatform(FName Name) override
|
|
{
|
|
GetTargetPlatforms(); // Populates PlatformsByName
|
|
|
|
if (ITargetPlatform** Platform = PlatformsByName.Find(Name))
|
|
{
|
|
return *Platform;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
virtual ITargetPlatform* FindTargetPlatform(const TCHAR* Name) override
|
|
{
|
|
return FindTargetPlatform(FName(Name));
|
|
}
|
|
|
|
virtual ITargetPlatform* FindTargetPlatformWithSupport(FName SupportType, FName RequiredSupportedValue)
|
|
{
|
|
// first try to find an active target platform. if that fails, try all target platforms.
|
|
// this gives priority to the active target platform if multiple platforms support the same value
|
|
for (int Pass = 0; Pass < 2; Pass++)
|
|
{
|
|
const TArray<ITargetPlatform*>& TargetPlatforms = (Pass == 0) ? GetActiveTargetPlatforms() : GetTargetPlatforms();
|
|
|
|
for (int32 Index = 0; Index < TargetPlatforms.Num(); Index++)
|
|
{
|
|
if (TargetPlatforms[Index]->SupportsValueForType(SupportType, RequiredSupportedValue))
|
|
{
|
|
return TargetPlatforms[Index];
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
virtual const TArray<ITargetPlatform*>& GetCookingTargetPlatforms() override
|
|
{
|
|
return GetActiveTargetPlatforms();
|
|
}
|
|
|
|
virtual const TArray<ITargetPlatform*>& GetActiveTargetPlatforms() override
|
|
{
|
|
static bool bInitialized = false;
|
|
static FRWLock InitializedLock;
|
|
static TArray<ITargetPlatform*> Results;
|
|
|
|
// Check if result is already initialized with lightweight read-only lock
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_ReadOnly);
|
|
if (bInitialized && !bForceCacheUpdate)
|
|
{
|
|
return Results;
|
|
}
|
|
}
|
|
|
|
// Initialize the result now
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_Write);
|
|
// Check if another thread initialized the result before this thread received the write lock
|
|
if (!bInitialized || bForceCacheUpdate)
|
|
{
|
|
bInitialized = InitializeActiveTargetPlatforms(Results);
|
|
}
|
|
}
|
|
|
|
return Results;
|
|
}
|
|
|
|
virtual bool RestrictFormatsToRuntimeOnly() override
|
|
{
|
|
GetActiveTargetPlatforms(); // make sure this is initialized
|
|
|
|
return bRestrictFormatsToRuntimeOnly;
|
|
}
|
|
|
|
virtual ITargetPlatform* GetRunningTargetPlatform() override
|
|
{
|
|
static bool bInitialized = false;
|
|
static FRWLock InitializedLock;
|
|
static ITargetPlatform* Result = nullptr;
|
|
|
|
// Check if result is already initialized with lightweight read-only lock
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_ReadOnly);
|
|
if (bInitialized && !bForceCacheUpdate)
|
|
{
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
// Initialize the result now
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_Write);
|
|
// Check if another thread initialized the result before this thread received the write lock
|
|
if (!bInitialized || bForceCacheUpdate)
|
|
{
|
|
Result = nullptr;
|
|
|
|
const TArray<ITargetPlatform*>& TargetPlatforms = GetTargetPlatforms();
|
|
|
|
for (int32 Index = 0; Index < TargetPlatforms.Num(); Index++)
|
|
{
|
|
if (TargetPlatforms[Index]->IsRunningPlatform())
|
|
{
|
|
// we should not have two running platforms
|
|
checkf((Result == nullptr),
|
|
TEXT("Found multiple running platforms.\n\t%s\nand\n\t%s"),
|
|
*Result->PlatformName(),
|
|
*TargetPlatforms[Index]->PlatformName()
|
|
);
|
|
Result = TargetPlatforms[Index];
|
|
bInitialized = (Result != nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
template<typename FormatType, typename FormatModuleType, typename HelperType>
|
|
const TArray<const FormatType*>& GetFormatsWithHints()
|
|
{
|
|
static bool bInitialized = false;
|
|
static FRWLock InitializedLock;
|
|
static TArray<const FormatType*> Results;
|
|
|
|
// Check if result is already initialized with lightweight read-only lock
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_ReadOnly);
|
|
if (bInitialized && !bForceCacheUpdate)
|
|
{
|
|
return Results;
|
|
}
|
|
}
|
|
|
|
// Initialize the result now
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_Write);
|
|
// Check if another thread initialized the result before this thread received the write lock
|
|
if (!bInitialized || bForceCacheUpdate)
|
|
{
|
|
InitializeFormatsWithHints<FormatType, FormatModuleType, HelperType>(Results);
|
|
bInitialized = true;
|
|
}
|
|
}
|
|
|
|
return Results;
|
|
}
|
|
|
|
struct FAudioHintHelper
|
|
{
|
|
static IAudioFormat* GetFormatFromModule(IAudioFormatModule* Module)
|
|
{
|
|
return Module->GetAudioFormat();
|
|
}
|
|
static const TCHAR* GetAllModuleWildcard()
|
|
{
|
|
return TEXT("*AudioFormat*");
|
|
}
|
|
static const TCHAR* GetFormatDesc()
|
|
{
|
|
return TEXT("audio");
|
|
}
|
|
#if WITH_ENGINE
|
|
static void GetHintedModules(ITargetPlatform* Platform, TArray<FName>& Hints)
|
|
{
|
|
Platform->GetWaveFormatModuleHints(Hints);
|
|
}
|
|
static void GetRequiredFormats(ITargetPlatform* Platform, TArray<FName>& RequiredFormats)
|
|
{
|
|
Platform->GetAllWaveFormats(RequiredFormats);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
struct FShaderHintHelper
|
|
{
|
|
static IShaderFormat* GetFormatFromModule(IShaderFormatModule* Module)
|
|
{
|
|
return Module->GetShaderFormat();
|
|
}
|
|
static const TCHAR* GetAllModuleWildcard()
|
|
{
|
|
return SHADERFORMAT_MODULE_WILDCARD;
|
|
}
|
|
static const TCHAR* GetFormatDesc()
|
|
{
|
|
return TEXT("shader");
|
|
}
|
|
#if WITH_ENGINE
|
|
static void GetHintedModules(ITargetPlatform* Platform, TArray<FName>& Hints)
|
|
{
|
|
Platform->GetShaderFormatModuleHints(Hints);
|
|
Hints.Add(TEXT("ShaderFormatVectorVM"));
|
|
}
|
|
static void GetRequiredFormats(ITargetPlatform* Platform, TArray<FName>& RequiredFormats)
|
|
{
|
|
Platform->GetAllTargetedShaderFormats(RequiredFormats);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
virtual const TArray<const IAudioFormat*>& GetAudioFormats() override
|
|
{
|
|
return GetFormatsWithHints<IAudioFormat, IAudioFormatModule, FAudioHintHelper>();
|
|
}
|
|
|
|
virtual const IAudioFormat* FindAudioFormat(FName Name) override
|
|
{
|
|
const TArray<const IAudioFormat*>& AudioFormats = GetAudioFormats();
|
|
|
|
for (int32 Index = 0; Index < AudioFormats.Num(); Index++)
|
|
{
|
|
TArray<FName> Formats;
|
|
|
|
AudioFormats[Index]->GetSupportedFormats(Formats);
|
|
|
|
for (int32 FormatIndex = 0; FormatIndex < Formats.Num(); FormatIndex++)
|
|
{
|
|
if (Formats[FormatIndex] == Name)
|
|
{
|
|
return AudioFormats[Index];
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
virtual const ITextureFormat* FindTextureFormat(FName Name) override
|
|
{
|
|
return TextureFormatManager->FindTextureFormat(Name);
|
|
}
|
|
|
|
virtual const TArray<const IShaderFormat*>& GetShaderFormats() override
|
|
{
|
|
//if (!AllowShaderCompiling())
|
|
//{
|
|
// static TArray<const IShaderFormat*> Empty;
|
|
// return Empty;
|
|
//}
|
|
|
|
return GetFormatsWithHints<IShaderFormat, IShaderFormatModule, FShaderHintHelper>();
|
|
}
|
|
|
|
virtual const IShaderFormat* FindShaderFormat(FName Name) override
|
|
{
|
|
return ::FindShaderFormat(Name, GetShaderFormats());
|
|
}
|
|
|
|
virtual uint32 ShaderFormatVersion(FName Name) override
|
|
{
|
|
static bool bInitialized = false;
|
|
static FRWLock InitializedLock;
|
|
static TMap<FName, uint32> FormatVersionCache;
|
|
|
|
// Check if result is already initialized with lightweight read-only lock
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_ReadOnly);
|
|
if (bInitialized && !bForceCacheUpdate)
|
|
{
|
|
return FindShaderFormatVersion(FormatVersionCache, Name);
|
|
}
|
|
}
|
|
|
|
// Initialize the result now
|
|
{
|
|
FRWScopeLock InitializedLockGuard(InitializedLock, FRWScopeLockType::SLT_Write);
|
|
// Check if another thread initialized the result before this thread received the write lock
|
|
if (!bInitialized || bForceCacheUpdate)
|
|
{
|
|
InitializeShaderFormatVersions(FormatVersionCache);
|
|
bInitialized = true;
|
|
}
|
|
}
|
|
|
|
return FindShaderFormatVersion(FormatVersionCache, Name);
|
|
}
|
|
|
|
virtual const TArray<const IPhysXCooking*>& GetPhysXCooking() override
|
|
{
|
|
static TArray<const IPhysXCooking*> Results;
|
|
|
|
return Results;
|
|
}
|
|
|
|
virtual const IPhysXCooking* FindPhysXCooking(FName Name) override
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
protected:
|
|
|
|
|
|
bool InitializeSinglePlatform(FName PlatformName, const FString& AutoSDKPath)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::InitializeSinglePlatform);
|
|
|
|
// try the incoming name as a module name, or as a platform name
|
|
FName PlatformModuleName = PlatformName;
|
|
//@todo Custom TargetPlatforms
|
|
FName PlatformControlsModuleName = *(PlatformName.ToString() + TEXT("TargetPlatformControls"));;
|
|
FName PlatformSettingsModuleName = *(PlatformName.ToString() + TEXT("TargetPlatformSettings"));;
|
|
|
|
ITargetPlatformModule* Module = nullptr;
|
|
ITargetPlatformControlsModule* ModuleControls = nullptr;
|
|
ITargetPlatformSettingsModule* ModuleSettings = nullptr;
|
|
|
|
FModuleManager& ModuleManager = FModuleManager::Get();
|
|
|
|
if (!ModuleManager.ModuleExists(*PlatformModuleName.ToString()))
|
|
{
|
|
PlatformModuleName = *(PlatformName.ToString() + TEXT("TargetPlatform"));
|
|
}
|
|
|
|
if (ModuleManager.ModuleExists(*PlatformModuleName.ToString()))
|
|
{
|
|
Module = FModuleManager::LoadModulePtr<ITargetPlatformModule>(PlatformModuleName);
|
|
}
|
|
|
|
if (ModuleManager.ModuleExists(*PlatformSettingsModuleName.ToString()))
|
|
{
|
|
ModuleSettings = FModuleManager::LoadModulePtr<ITargetPlatformSettingsModule>(PlatformSettingsModuleName);
|
|
}
|
|
|
|
if (ModuleManager.ModuleExists(*PlatformControlsModuleName.ToString()))
|
|
{
|
|
ModuleControls = FModuleManager::LoadModulePtr<ITargetPlatformControlsModule>(PlatformControlsModuleName);
|
|
}
|
|
|
|
if(ModuleSettings != nullptr)
|
|
{
|
|
TArray<ITargetPlatformSettings*> TargetPlatformSettings = ModuleSettings->GetTargetPlatformSettings();
|
|
for (ITargetPlatformSettings* Platform : TargetPlatformSettings)
|
|
{
|
|
PlatformSettings.Add(Platform);
|
|
if (Module != nullptr)
|
|
{
|
|
Module->PlatformSettings.Add(Platform);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Module == nullptr)
|
|
{
|
|
if (ModuleManager.ModuleExists(*PlatformModuleName.ToString()))
|
|
{
|
|
// Retry module load with error logging enabled
|
|
EModuleLoadResult Result;
|
|
Module = static_cast<ITargetPlatformModule*>(ModuleManager.LoadModuleWithFailureReason(PlatformModuleName, Result, ELoadModuleFlags::LogFailures));
|
|
UE_CLOGFMT(!Module, LogTargetPlatformManager, Warning,
|
|
"Failed to load module '{PlatformModuleName}' for platform {PlatformName} (Reason={Reason}, AutoSDKPath='{AutoSDKPath}').",
|
|
PlatformModuleName, PlatformName.ToString(), LexToString(Result), AutoSDKPath);
|
|
}
|
|
else
|
|
{
|
|
UE_LOGFMT(LogTargetPlatformManager, Log,
|
|
"Failed to load module '{PlatformModuleName}' for platform {PlatformName} (Reason={Reason}, AutoSDKPath='{AutoSDKPath}').",
|
|
PlatformModuleName, PlatformName.ToString(), LexToString(EModuleLoadResult::FileNotFound), AutoSDKPath);
|
|
}
|
|
}
|
|
|
|
// original logic for module loading here
|
|
if (Module)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::InitializeSinglePlatform Loading);
|
|
|
|
// would like to move this check to GetActiveTargetPlatforms, but too many things cache this result
|
|
// this setup will become faster after TTP 341897 is complete.
|
|
RETRY_SETUPANDVALIDATE:
|
|
if (AutoSDKPath == TEXT("") || SetupAndValidateAutoSDK(AutoSDKPath))
|
|
{
|
|
|
|
if (ModuleControls != nullptr)
|
|
{
|
|
TArray<ITargetPlatformControls*> TargetPlatformControls = ModuleControls->GetTargetPlatformControls(PlatformSettingsModuleName);
|
|
for (ITargetPlatformControls* Platform : TargetPlatformControls)
|
|
{
|
|
PlatformControls.Add(Platform);
|
|
Module->PlatformControls.Add(Platform);
|
|
}
|
|
}
|
|
|
|
TArray<ITargetPlatform*> TargetPlatforms = Module->GetTargetPlatforms();
|
|
for (ITargetPlatform* Platform : TargetPlatforms)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Display, TEXT("Loaded TargetPlatform '%s'"), *Platform->PlatformName());
|
|
Platforms.Add(Platform);
|
|
PlatformsByName.Add(FName(Platform->PlatformName()), Platform);
|
|
}
|
|
|
|
// only success path
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// this hack is here because if you try and setup and validate autosdk some times it will fail because shared files are in use by another child cooker
|
|
static bool bIsChildCooker = FParse::Param(FCommandLine::Get(), TEXT("cookchild"));
|
|
if (bIsChildCooker)
|
|
{
|
|
static int Counter = 0;
|
|
++Counter;
|
|
if (Counter < 10)
|
|
{
|
|
goto RETRY_SETUPANDVALIDATE;
|
|
}
|
|
}
|
|
UE_LOG(LogTargetPlatformManager, Display, TEXT("Failed to SetupAndValidateAutoSDK for platform '%s'"), *PlatformName.ToString());
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Discovers the available target platforms. */
|
|
void DiscoverAvailablePlatforms()
|
|
{
|
|
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("FTargetPlatformManagerModule::DiscoverAvailablePlatforms"), STAT_FTargetPlatformManagerModule_DiscoverAvailablePlatforms, STATGROUP_TargetPlatform);
|
|
|
|
Platforms.Empty(Platforms.Num());
|
|
PlatformsByName.Empty(PlatformsByName.Num());
|
|
|
|
#if !IS_MONOLITHIC
|
|
// Find all module subdirectories and add them so we can load dependent modules for target platform modules
|
|
// We may not be able to restrict this to subdirectories found in FPlatformInfo because we could have a subdirectory
|
|
// that is not one of these platforms. Imagine a "Sega" shared directory for the "Genesis" and "Dreamcast" platforms
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::DiscoverAvailablePlatforms FindFilesRecursive);
|
|
|
|
TArray<FString> ModuleSubdirs;
|
|
IFileManager::Get().FindFilesRecursive(ModuleSubdirs, *FPlatformProcess::GetModulesDirectory(), TEXT("*"), false, true);
|
|
for (const FString& ModuleSubdir : ModuleSubdirs)
|
|
{
|
|
FModuleManager::Get().AddBinariesDirectory(*ModuleSubdir, false);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Get the platform we are previewing if GMaxRHIShaderPlatform is a preview SP
|
|
#if WITH_EDITOR
|
|
FName PlatformNamePreview = NAME_None;
|
|
if (IsRunningGame())
|
|
{
|
|
if (FDataDrivenShaderPlatformInfo::GetIsPreviewPlatform(GMaxRHIShaderPlatform))
|
|
{
|
|
PlatformNamePreview = FDataDrivenShaderPlatformInfo::GetPlatformName(GMaxRHIShaderPlatform);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// find a set of valid target platform names (the platform DataDrivenPlatformInfo.ini file was found indicates support for the platform
|
|
// exists on disk, so the TP is expected to work)
|
|
FScopedSlowTask SlowTask((float)FDataDrivenPlatformInfoRegistry::GetAllPlatformInfos().Num());
|
|
for (auto Pair : FDataDrivenPlatformInfoRegistry::GetAllPlatformInfos())
|
|
{
|
|
FName PlatformName = Pair.Key;
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*PlatformName.ToString());
|
|
|
|
const FDataDrivenPlatformInfo& Info = Pair.Value;
|
|
|
|
SlowTask.EnterProgressFrame(1);
|
|
|
|
#if WITH_EDITOR
|
|
// if we have the editor and we are using -game (but not with -platformrhi)
|
|
// only need to instantiate the current platform if we are not using OverrideSP command
|
|
// If we are using OverrideSP command that means our SP is a preview SP
|
|
// Because of that we must also load the TPS of the Platform we are previewing.
|
|
if (IsRunningGame())
|
|
{
|
|
static bool bUsingPlatformRHI = FString(FCommandLine::Get()).Contains(TEXT("-platformrhi="));
|
|
if (!bUsingPlatformRHI && PlatformName != FPlatformProperties::IniPlatformName() &&
|
|
(PlatformName != PlatformNamePreview))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (Info.bEnabledForUse)
|
|
{
|
|
InitializeSinglePlatform(PlatformName, Info.AutoSDKPath);
|
|
}
|
|
}
|
|
|
|
TArray<FString> CustomTargetPlatformModules;
|
|
GConfig->GetArray(TEXT("CustomTargetPlatforms"), TEXT("ModuleName"), CustomTargetPlatformModules, GEditorIni);
|
|
for (const FString& ModuleName : CustomTargetPlatformModules)
|
|
{
|
|
InitializeSinglePlatform(*ModuleName, TEXT(""));
|
|
}
|
|
|
|
|
|
if (!Platforms.Num())
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Error, TEXT("No target platforms found!"));
|
|
}
|
|
}
|
|
|
|
bool UpdatePlatformEnvironment(const FString& PlatformName, TArray<FString> &Keys, TArray<FString> &Values) override
|
|
{
|
|
SetupEnvironmentVariables(Keys, Values);
|
|
return SetupSDKStatus(PlatformName);
|
|
}
|
|
|
|
bool SetupAndValidateAutoSDK(const FString& AutoSDKPath)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::SetupAndValidateAutoSDK);
|
|
|
|
#if AUTOSDKS_ENABLED
|
|
bool bValidSDK = false;
|
|
if (AutoSDKPath.Len() > 0)
|
|
{
|
|
FName PlatformFName(*AutoSDKPath);
|
|
|
|
// cache result of the last setup attempt to avoid calling UBT all the time.
|
|
bool* bPreviousSetupSuccessful = PlatformsSetup.Find(PlatformFName);
|
|
if (bPreviousSetupSuccessful)
|
|
{
|
|
bValidSDK = *bPreviousSetupSuccessful;
|
|
}
|
|
else
|
|
{
|
|
bValidSDK = SetupEnvironmentFromAutoSDK(AutoSDKPath);
|
|
PlatformsSetup.Add(PlatformFName, bValidSDK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if a platform has no AutoSDKPath, then just assume the SDK is installed, we have no basis for determining it.
|
|
bValidSDK = true;
|
|
}
|
|
return bValidSDK;
|
|
#else
|
|
return true;
|
|
#endif // AUTOSDKS_ENABLED
|
|
}
|
|
|
|
bool SetupEnvironmentFromManualSDK(const FString& EnvVarFileName)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::SetupEnvironmentFromManualSDK);
|
|
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Reading the manifest for auto-selected manual sdks") );
|
|
bool bResult = SetupEnvironmentFromEnvVarFile(EnvVarFileName);
|
|
IFileManager::Get().Delete(*EnvVarFileName);
|
|
return bResult;
|
|
}
|
|
|
|
bool SetupEnvironmentFromAutoSDK(const FString& AutoSDKPath)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::SetupEnvironmentFromAutoSDK);
|
|
|
|
#if AUTOSDKS_ENABLED
|
|
|
|
if (!UE::AutoSDK::IsAutoSDKsEnabled())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Invoke UBT to perform SDK switching, or detect that a proper manual SDK is already setup.
|
|
#if PLATFORM_WINDOWS
|
|
FString HostPlatform(TEXT("HostWin64"));
|
|
#else
|
|
FString HostPlatform = FString::Printf(TEXT("Host%hs"), FPlatformProperties::IniPlatformName());
|
|
#endif
|
|
|
|
FString TargetSDKRoot = UE::AutoSDK::GetAutoSDKPlatformDir(*AutoSDKPath);
|
|
FString SDKInstallManifestFilePath = TargetSDKRoot / UE::AutoSDK::SDKInstallManifestFileName;
|
|
|
|
TArray<FString> FileLines;
|
|
// If we are using a manual install, then it is valid for there to be no OutputEnvVars file, so
|
|
// check for ManualINstall in the CurrentlyInstalled file
|
|
if (FFileHelper::LoadFileToStringArray(FileLines, *SDKInstallManifestFilePath))
|
|
{
|
|
if (FileLines.Num() != 2 && FileLines.Num() != 3)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Warning, TEXT("Malformed install manifest file for Platform %s"), *AutoSDKPath);
|
|
return false;
|
|
}
|
|
|
|
static const FString ManualSDKString(TEXT("ManualSDK"));
|
|
if (FileLines[1].Compare(ManualSDKString, ESearchCase::IgnoreCase) == 0)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Platform %s has manual sdk install"), *AutoSDKPath);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Install manifest file for Platform %s not found. Platform not set up."), *AutoSDKPath);
|
|
return false;
|
|
}
|
|
|
|
static const FString SDKEnvironmentVarsFile(TEXT("OutputEnvVars.txt"));
|
|
FString EnvVarFileName = FPaths::Combine(*TargetSDKRoot, *SDKEnvironmentVarsFile);
|
|
|
|
if (!SetupEnvironmentFromEnvVarFile(EnvVarFileName))
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Warning, TEXT("OutputEnvVars.txt not found for platform: '%s'"), *AutoSDKPath);
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Platform %s has auto sdk install"), *AutoSDKPath);
|
|
return true;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool SetupEnvironmentFromEnvVarFile( const FString& EnvVarFileName )
|
|
{
|
|
// If we are using a manual install, then it is valid for there to be no OutputEnvVars file.
|
|
TUniquePtr<FArchive> EnvVarFile(IFileManager::Get().CreateFileReader(*EnvVarFileName));
|
|
if (EnvVarFile)
|
|
{
|
|
TArray<FString> FileLines;
|
|
{
|
|
int64 FileSize = EnvVarFile->TotalSize();
|
|
int64 MemSize = FileSize + 1;
|
|
void* FileMem = FMemory::Malloc(MemSize);
|
|
FMemory::Memset(FileMem, 0, MemSize);
|
|
|
|
EnvVarFile->Serialize(FileMem, FileSize);
|
|
|
|
FString FileAsString(ANSI_TO_TCHAR(FileMem));
|
|
FileAsString.ParseIntoArrayLines(FileLines);
|
|
|
|
FMemory::Free(FileMem);
|
|
EnvVarFile->Close();
|
|
}
|
|
|
|
TArray<FString> PathAdds;
|
|
TArray<FString> PathRemoves;
|
|
TArray<FString> EnvVarNames;
|
|
TArray<FString> EnvVarValues;
|
|
|
|
const FString VariableSplit(TEXT("="));
|
|
for (int32 i = 0; i < FileLines.Num(); ++i)
|
|
{
|
|
const FString& VariableString = FileLines[i];
|
|
|
|
FString Left;
|
|
FString Right;
|
|
VariableString.Split(VariableSplit, &Left, &Right);
|
|
|
|
if (Left.Compare(TEXT("strippath"), ESearchCase::IgnoreCase) == 0)
|
|
{
|
|
PathRemoves.Add(Right);
|
|
}
|
|
else if (Left.Compare(TEXT("addpath"), ESearchCase::IgnoreCase) == 0)
|
|
{
|
|
PathAdds.Add(Right);
|
|
}
|
|
else
|
|
{
|
|
// convenience for setup.bat writers. Trim any accidental whitespace from var names/values.
|
|
EnvVarNames.Add(Left.TrimStartAndEnd());
|
|
EnvVarValues.Add(Right.TrimStartAndEnd());
|
|
}
|
|
}
|
|
|
|
// don't actually set anything until we successfully validate and read all values in.
|
|
// we don't want to set a few vars, return a failure, and then have a platform try to
|
|
// build against a manually installed SDK with half-set env vars.
|
|
SetupEnvironmentVariables(EnvVarNames, EnvVarValues);
|
|
|
|
|
|
FString OrigPathVar = FPlatformMisc::GetEnvironmentVariable(TEXT("PATH"));
|
|
|
|
// actually perform the PATH stripping / adding.
|
|
const TCHAR* PathDelimiter = FPlatformMisc::GetPathVarDelimiter();
|
|
TArray<FString> PathVars;
|
|
OrigPathVar.ParseIntoArray(PathVars, PathDelimiter, true);
|
|
|
|
TArray<FString> ModifiedPathVars;
|
|
ModifiedPathVars = PathVars;
|
|
|
|
// perform removes first, in case they overlap with any adds.
|
|
for (int32 PathRemoveIndex = 0; PathRemoveIndex < PathRemoves.Num(); ++PathRemoveIndex)
|
|
{
|
|
const FString& PathRemove = PathRemoves[PathRemoveIndex];
|
|
for (int32 PathVarIndex = 0; PathVarIndex < PathVars.Num(); ++PathVarIndex)
|
|
{
|
|
const FString& PathVar = PathVars[PathVarIndex];
|
|
if (PathVar.Find(PathRemove, ESearchCase::IgnoreCase) >= 0)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Removing Path: '%s'"), *PathVar);
|
|
ModifiedPathVars.Remove(PathVar);
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove all the of ADDs so that if this function is executed multiple times, the paths will be guarateed to be in the same order after each run.
|
|
// If we did not do this, a 'remove' that matched some, but not all, of our 'adds' would cause the order to change.
|
|
for (int32 PathAddIndex = 0; PathAddIndex < PathAdds.Num(); ++PathAddIndex)
|
|
{
|
|
const FString& PathAdd = PathAdds[PathAddIndex];
|
|
for (int32 PathVarIndex = 0; PathVarIndex < PathVars.Num(); ++PathVarIndex)
|
|
{
|
|
const FString& PathVar = PathVars[PathVarIndex];
|
|
if (PathVar.Find(PathAdd, ESearchCase::IgnoreCase) >= 0)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Removing Path: '%s'"), *PathVar);
|
|
ModifiedPathVars.Remove(PathVar);
|
|
}
|
|
}
|
|
}
|
|
|
|
// perform adds, but don't add duplicates
|
|
for (int32 PathAddIndex = 0; PathAddIndex < PathAdds.Num(); ++PathAddIndex)
|
|
{
|
|
const FString& PathAdd = PathAdds[PathAddIndex];
|
|
if (!ModifiedPathVars.Contains(PathAdd))
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Adding Path: '%s'"), *PathAdd);
|
|
ModifiedPathVars.Add(PathAdd);
|
|
}
|
|
}
|
|
|
|
FString ModifiedPath = FString::Join(ModifiedPathVars, PathDelimiter);
|
|
FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SetupSDKStatus()
|
|
{
|
|
return SetupSDKStatus(TEXT(""));
|
|
}
|
|
|
|
bool SetupSDKStatus(const FString& TargetPlatforms)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::SetupSDKStatus);
|
|
|
|
// FDataDrivenPlatformInfoRegistry::UpdateSdkStatus();
|
|
#if 0
|
|
DECLARE_SCOPE_CYCLE_COUNTER( TEXT( "FTargetPlatformManagerModule::SetupSDKStatus" ), STAT_FTargetPlatformManagerModule_SetupSDKStatus, STATGROUP_TargetPlatform );
|
|
|
|
// run UBT with -validate -allplatforms and read the output
|
|
FString CmdExe = TEXT("{EngineDir}/Binaries/DotNET/UnrealBuildTool.exe");
|
|
FString CommandLine = TEXT("-Mode=ValidatePlatforms");
|
|
// Allow for only a subset of platforms to be reparsed - needed when kicking a change from the UI
|
|
CommandLine += TargetPlatforms.IsEmpty() ? TEXT(" -allplatforms") : (TEXT(" -platforms=") + TargetPlatforms);
|
|
|
|
// convert into appropriate calls for the current platform
|
|
FPlatformProcess::ModifyCreateProcParams(CmdExe, CommandLine, FGenericPlatformProcess::ECreateProcHelperFlags::None);
|
|
|
|
|
|
TSharedPtr<FMonitoredProcess> UBTProcess = MakeShareable(new FMonitoredProcess(CmdExe, CommandLine, true));
|
|
UBTProcess->OnOutput().BindStatic(&FTargetPlatformManagerModule::OnStatusOutput);
|
|
SDKStatusMessage = TEXT("");
|
|
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::SetupSDKStatus WaitUntilUBTStarted);
|
|
UBTProcess->Launch();
|
|
while(UBTProcess->Update())
|
|
{
|
|
FPlatformProcess::Sleep(0.01f);
|
|
}
|
|
}
|
|
|
|
TArray<FString> PlatArray;
|
|
SDKStatusMessage.ParseIntoArrayWS(PlatArray);
|
|
for (int Index = 0; Index < PlatArray.Num()-2; ++Index)
|
|
{
|
|
FString Item = PlatArray[Index];
|
|
if (PlatArray[Index].Contains(TEXT("##PlatformValidate:")))
|
|
{
|
|
PlatformInfo::EPlatformSDKStatus Status = PlatArray[Index+2].Contains(TEXT("INVALID")) ? PlatformInfo::EPlatformSDKStatus::NotInstalled : PlatformInfo::EPlatformSDKStatus::Installed;
|
|
FString PlatformName = PlatArray[Index+1];
|
|
if (PlatformName == TEXT("Win64"))
|
|
{
|
|
PlatformName = TEXT("WindowsEditor");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("Windows");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("WindowsClient");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("WindowsServer");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
}
|
|
else if (PlatformName == TEXT("Mac"))
|
|
{
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("Mac");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("MacClient");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("MacServer");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
}
|
|
else if (PlatformName == TEXT("Linux"))
|
|
{
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("Linux");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("LinuxClient");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("LinuxServer");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
}
|
|
else if (PlatformName == TEXT("LinuxArm64"))
|
|
{
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("LinuxArm64");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("LinuxArm64Client");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
PlatformName = TEXT("LinuxArm64Server");
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
}
|
|
else if (PlatformName == TEXT("Desktop"))
|
|
{
|
|
// since Desktop is just packaging, we don't need an SDK, and UBT will return INVALID, since it doesn't build for it
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, PlatformInfo::EPlatformSDKStatus::Installed);
|
|
}
|
|
else
|
|
{
|
|
PlatformInfo::UpdatePlatformSDKStatus(PlatformName, Status);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool UpdateAfterSDKInstall(FName PlatformName)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FTargetPlatformManagerModule::UpdateAfterSDKInstall);
|
|
|
|
const FDataDrivenPlatformInfo& Info = FDataDrivenPlatformInfoRegistry::GetPlatformInfo(PlatformName);
|
|
|
|
#if AUTOSDKS_ENABLED
|
|
FString AutoSDKPath = Info.AutoSDKPath;
|
|
FName AutoSDKName(*AutoSDKPath);
|
|
if (AutoSDKName != NAME_None)
|
|
{
|
|
// make sure we can re-do the AutoSDK setup
|
|
PlatformsSetup.Remove(AutoSDKName);
|
|
}
|
|
#endif
|
|
|
|
// note: this assumes, along with other Turnkey code, that there is a TargetPlatform named with the IniPlatformName
|
|
ITargetPlatform* TargetPlatform = FindTargetPlatform(PlatformName);
|
|
|
|
bool bTPInitialized = false;
|
|
// if we didn't have a TP before, discover it now, it will do everything we need)
|
|
if (TargetPlatform == nullptr)
|
|
{
|
|
// create the TP(s) that weren't around before due to a bad SDK
|
|
if (Info.bEnabledForUse)
|
|
{
|
|
bTPInitialized = InitializeSinglePlatform(PlatformName, Info.AutoSDKPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if AUTOSDKS_ENABLED
|
|
if (AutoSDKName != NAME_None)
|
|
{
|
|
// setup AutoSDK, and then re-initialize the TP
|
|
SetupAndValidateAutoSDK(AutoSDKPath);
|
|
}
|
|
#endif
|
|
|
|
bTPInitialized = TargetPlatform->InitializeHostPlatform();
|
|
}
|
|
|
|
#if UE_WITH_TURNKEY_SUPPORT
|
|
if (bTPInitialized)
|
|
{
|
|
ITurnkeySupportModule::Get().UpdateSdkInfo();
|
|
}
|
|
|
|
ITurnkeySupportModule::Get().ClearDeviceStatus(PlatformName);
|
|
#endif
|
|
Invalidate();
|
|
|
|
return bTPInitialized;
|
|
}
|
|
|
|
private:
|
|
|
|
void SetupEnvironmentVariables(TArray<FString> &EnvVarNames, const TArray<FString>& EnvVarValues)
|
|
{
|
|
for (int i = 0; i < EnvVarNames.Num(); ++i)
|
|
{
|
|
const FString& EnvVarName = EnvVarNames[i];
|
|
const FString& EnvVarValue = EnvVarValues[i];
|
|
UE_LOG(LogTargetPlatformManager, Verbose, TEXT("Setting variable '%s' to '%s'."), *EnvVarName, *EnvVarValue);
|
|
FPlatformMisc::SetEnvironmentVar(*EnvVarName, *EnvVarValue);
|
|
}
|
|
}
|
|
|
|
void ModulesChangesCallback(FName ModuleName, EModuleChangeReason ReasonForChange)
|
|
{
|
|
if (!bIgnoreFirstDelegateCall && ModuleName.ToString().Contains(TEXT("TargetPlatform")) && !ModuleName.ToString().Contains(TEXT("ProjectTargetPlatformEditor")))
|
|
{
|
|
Invalidate();
|
|
}
|
|
bIgnoreFirstDelegateCall = false;
|
|
}
|
|
|
|
bool InitializeActiveTargetPlatforms(TArray<ITargetPlatform*>& OutResults)
|
|
{
|
|
bHasInitErrors = false; // If we had errors before, reset the flag and see later in this function if there are errors.
|
|
InitErrorMessages.Empty();
|
|
|
|
OutResults.Empty(OutResults.Num());
|
|
|
|
const TArray<ITargetPlatform*>& TargetPlatforms = GetTargetPlatforms();
|
|
|
|
FString PlatformStr;
|
|
|
|
if (FParse::Value(FCommandLine::Get(), TEXT("TARGETPLATFORM="), PlatformStr))
|
|
{
|
|
if (PlatformStr == TEXT("None"))
|
|
{
|
|
}
|
|
else if (PlatformStr == TEXT("All"))
|
|
{
|
|
OutResults = TargetPlatforms;
|
|
}
|
|
else
|
|
{
|
|
TArray<FString> PlatformNames;
|
|
|
|
PlatformStr.ParseIntoArray(PlatformNames, TEXT("+"), true);
|
|
|
|
for (int32 Index = 0; Index < TargetPlatforms.Num(); Index++)
|
|
{
|
|
if (PlatformNames.Contains(TargetPlatforms[Index]->PlatformName()))
|
|
{
|
|
OutResults.Add(TargetPlatforms[Index]);
|
|
}
|
|
}
|
|
|
|
if (OutResults.Num() == 0)
|
|
{
|
|
// An invalid platform was specified...
|
|
// Inform the user.
|
|
TStringBuilder<1024> AvailablePlatforms;
|
|
for (int32 Index = 0; Index < TargetPlatforms.Num(); Index++)
|
|
{
|
|
if (Index > 0)
|
|
{
|
|
AvailablePlatforms << TEXT(", ");
|
|
}
|
|
AvailablePlatforms << TargetPlatforms[Index]->PlatformName();
|
|
}
|
|
bHasInitErrors = true;
|
|
InitErrorMessages.Appendf(TEXT("Invalid target platform specified (%s). Available = { %s } "), *PlatformStr, *AvailablePlatforms);
|
|
UE_LOG(LogTargetPlatformManager, Error, TEXT("Invalid target platform specified (%s). Available = { %s } "), *PlatformStr, *AvailablePlatforms);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if there is no argument, use the current platform and only build formats that are actually needed to run.
|
|
bRestrictFormatsToRuntimeOnly = true;
|
|
|
|
for (int32 Index = 0; Index < TargetPlatforms.Num(); Index++)
|
|
{
|
|
if (TargetPlatforms[Index]->IsRunningPlatform())
|
|
{
|
|
OutResults.Add(TargetPlatforms[Index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!OutResults.Num())
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Display, TEXT("Not building assets for any platform."));
|
|
}
|
|
else
|
|
{
|
|
for (int32 Index = 0; Index < OutResults.Num(); Index++)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Display, TEXT("Building Assets For %s"), *OutResults[Index]->PlatformName());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<typename FormatType, typename FormatModuleType, typename HelperType>
|
|
void InitializeFormatsWithHints(TArray<const FormatType*>& OutResults)
|
|
{
|
|
OutResults.Empty(OutResults.Num());
|
|
|
|
// the functions for dealing with hints are only defined with Engine
|
|
#if WITH_ENGINE
|
|
TArray<FName> SupportedFormatsByHints;
|
|
TSet<FName> RequiredFormats;
|
|
// gather the hinted formats, and the needed formats for all the active targetplatforms
|
|
TArray<ITargetPlatform*> TargetPlatforms = GetTargetPlatforms();
|
|
for (ITargetPlatform* Platform : TargetPlatforms)
|
|
{
|
|
TArray<FName> FormatHints;
|
|
HelperType::GetHintedModules(Platform, FormatHints);
|
|
for (FName HintedModuleName : FormatHints)
|
|
{
|
|
FormatModuleType* Module = FModuleManager::Get().LoadModulePtr<FormatModuleType>(HintedModuleName);
|
|
if (Module != nullptr)
|
|
{
|
|
FormatType* Format = HelperType::GetFormatFromModule(Module);
|
|
if (Format && !OutResults.Contains(Format))
|
|
{
|
|
// remember the module
|
|
OutResults.Add(Format);
|
|
// remember its formats
|
|
Format->GetSupportedFormats(SupportedFormatsByHints);
|
|
}
|
|
}
|
|
}
|
|
|
|
// remember the formats the TP needs
|
|
TArray<FName> PlatformRequiredFormats;
|
|
HelperType::GetRequiredFormats(Platform, PlatformRequiredFormats);
|
|
RequiredFormats.Append(PlatformRequiredFormats);
|
|
}
|
|
|
|
// make sure every required format was found above
|
|
bool bFoundAllFormats = true;
|
|
for (FName Format : RequiredFormats)
|
|
{
|
|
if (!SupportedFormatsByHints.Contains(Format))
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Unable to find %s format %s from hinted modules, loading all potential format modules to find it"), HelperType::GetFormatDesc(), *Format.ToString());
|
|
bFoundAllFormats = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we found all the formats from the hints, we are done, and Results is filled out
|
|
if (bFoundAllFormats)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// if the hints weren't enough to find everything, then load all modules
|
|
TArray<FName> Modules;
|
|
FModuleManager::Get().FindModules(HelperType::GetAllModuleWildcard(), Modules);
|
|
|
|
if (!Modules.Num())
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Error, TEXT("No target %s formats found!"), HelperType::GetFormatDesc());
|
|
}
|
|
|
|
for (int32 Index = 0; Index < Modules.Num(); Index++)
|
|
{
|
|
FormatModuleType* Module = FModuleManager::LoadModulePtr<FormatModuleType>(Modules[Index]);
|
|
if (Module)
|
|
{
|
|
FormatType* Format = HelperType::GetFormatFromModule(Module);
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT("Loaded format module %s"), *Modules[Index].ToString());
|
|
if (Format != nullptr)
|
|
{
|
|
TArray<FName> Formats;
|
|
Format->GetSupportedFormats(Formats);
|
|
for (FName Name : Formats)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Log, TEXT(" %s"), *Name.ToString());
|
|
}
|
|
|
|
OutResults.AddUnique(Format);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitializeShaderFormatVersions(TMap<FName, uint32>& OutFormatVersionCache)
|
|
{
|
|
OutFormatVersionCache.Reset();
|
|
|
|
for (const IShaderFormat* SF : GetShaderFormats())
|
|
{
|
|
TArray<FName> Formats;
|
|
SF->GetSupportedFormats(Formats);
|
|
for (FName FormatName : Formats)
|
|
{
|
|
OutFormatVersionCache.FindOrAdd(FormatName, SF->GetVersion(FormatName));
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 FindShaderFormatVersion(const TMap<FName, uint32>& FormatVersionCache, FName Name) const
|
|
{
|
|
const uint32* Result = FormatVersionCache.Find(Name);
|
|
if (!Result)
|
|
{
|
|
UE_LOG(LogTargetPlatformManager, Fatal, TEXT("ShaderFormat not found for %s! Dynamically loaded shader formats require invalidation of FormatVersionCache."), *Name.ToString());
|
|
return INDEX_NONE;
|
|
}
|
|
return *Result;
|
|
}
|
|
|
|
static FString SDKStatusMessage;
|
|
static void OnStatusOutput(FString Message)
|
|
{
|
|
SDKStatusMessage += Message;
|
|
}
|
|
|
|
FString InitErrorMessages;
|
|
|
|
// Delegate used to notify users of returned ITargetPlatform* pointers when those pointers are destructed due to a call to Invalidate
|
|
FOnTargetPlatformsInvalidated OnTargetPlatformsInvalidated;
|
|
|
|
// If true we should build formats that are actually required for use by the runtime.
|
|
// This happens for an ordinary editor run and more specifically whenever there is no
|
|
// TargetPlatform= on the command line.
|
|
bool bRestrictFormatsToRuntimeOnly;
|
|
|
|
// Flag to force reinitialization of all cached data. This is needed to have up-to-date caches
|
|
// in case of a module reload of a TargetPlatform-Module.
|
|
bool bForceCacheUpdate;
|
|
|
|
// Flag to indicate that there were errors during initialization
|
|
bool bHasInitErrors;
|
|
|
|
// Flag to avoid redunant reloads
|
|
bool bIgnoreFirstDelegateCall;
|
|
|
|
// Holds the list of discovered platforms.
|
|
TArray<ITargetPlatform*> Platforms;
|
|
TArray<ITargetPlatformControls*> PlatformControls;
|
|
TArray<ITargetPlatformSettings*> PlatformSettings;
|
|
|
|
// Map for fast lookup of platforms by name.
|
|
TMap<FName, ITargetPlatform*> PlatformsByName;
|
|
|
|
// External module that texture format operations are forwarded to
|
|
ITextureFormatManagerModule* TextureFormatManager;
|
|
|
|
#if AUTOSDKS_ENABLED
|
|
// holds the list of Platforms that have attempted setup.
|
|
TMap<FName, bool> PlatformsSetup;
|
|
#endif
|
|
};
|
|
|
|
|
|
FString FTargetPlatformManagerModule::SDKStatusMessage = TEXT("");
|
|
|
|
|
|
IMPLEMENT_MODULE(FTargetPlatformManagerModule, TargetPlatform);
|