// Copyright Epic Games, Inc. All Rights Reserved. // .... #pragma once #include "CoreMinimal.h" #include "RHIDefinitions.h" #include "HAL/PlatformProcess.h" #include "HAL/FileManager.h" #include "Misc/Paths.h" #include "RHIFeatureLevel.h" #include "RHIShaderPlatform.h" // #ifndef UE_METAL_USE_METAL_SHADER_CONVERTER #define UE_METAL_USE_METAL_SHADER_CONVERTER PLATFORM_SUPPORTS_BINDLESS_RENDERING #endif // UE_METAL_USE_METAL_SHADER_CONVERTER // IOS and TVOS use the mobile toolchain. enum EAppleSDKType { AppleSDKMac, AppleSDKMobile, AppleSDKCount, }; static FName NAME_SF_METAL_ES3_1_IOS(TEXT("SF_METAL_ES3_1_IOS")); static FName NAME_SF_METAL_SM5_IOS(TEXT("SF_METAL_SM5_IOS")); static FName NAME_SF_METAL_ES3_1_TVOS(TEXT("SF_METAL_ES3_1_TVOS")); static FName NAME_SF_METAL_SM5_TVOS(TEXT("SF_METAL_SM5_TVOS")); static FName NAME_SF_METAL_SM5(TEXT("SF_METAL_SM5")); static FName NAME_SF_METAL_SM6(TEXT("SF_METAL_SM6")); static FName NAME_SF_METAL_SIM(TEXT("SF_METAL_SIM")); static FName NAME_SF_METAL_ES3_1(TEXT("SF_METAL_ES3_1")); DECLARE_LOG_CATEGORY_EXTERN(LogMetalCompilerSetup, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogMetalShaderCompiler, Log, All); class FMetalCompilerToolchain { public: enum class EMetalToolchainStatus : int32 { Success, ToolchainNotFound, CouldNotParseCompilerVersion, CouldNotParseTargetVersion, CouldNotFindMetalStdLib, }; struct PackedVersion { union { struct { int32 Major : 16; int32 Minor : 8; int32 Patch : 8; }; int32 Version; }; }; // Initializes this toolchain. void Init(); // Tears down the toolchain void Teardown(); // Takes a Job and compiles a shader. Produces .air and .metallib bool CompileMetalShader(struct FMetalShaderBytecodeJob& Job, struct FMetalShaderBytecode& Output) const; // Executes 'Command' on the local machine bool ExecGenericCommand(const TCHAR* Command, const TCHAR* Params, int32* OutReturnCode, FString* OutStdOut, FString* OutStdErr, bool bIsConsoleApp = false) const; // Executes the metal frontend compiler for 'SDK' on the local or remote machine, depending on the current configuration bool ExecMetalFrontend(EAppleSDKType SDK, const TCHAR* Parameters, int32* OutReturnCode, FString* OutStdOut, FString* OutStdErr) const; // Executes metallib for 'SDK' on the local or remote machine, depending on configuration bool ExecMetalLib(EAppleSDKType SDK, const TCHAR* Parameters, int32* OutReturnCode, FString* OutStdOut, FString* OutStdErr) const; // Executes metal-ar for 'SDK' on the local or remote machine, depending on configuration bool ExecMetalAr(EAppleSDKType SDK, const TCHAR* ScriptFile, int32* OutReturnCode, FString* OutStdOut, FString* OutStdErr) const; // Executes air-pack for 'SDK' on the local or remote machine, depending on configuration bool ExecAirPack(EAppleSDKType SDK, const TCHAR* Parameters, int32* OutReturnCode, FString* OutStdOut, FString* OutStdErr) const; // This toolchain is set up correctly and ready to use. bool IsCompilerAvailable() const { return this->bToolchainAvailable; } // The version of the compiler, given by metal -v PackedVersion GetCompilerVersion(EShaderPlatform Platform) const; // The AIR target version, given by metal -v PackedVersion GetTargetVersion(EShaderPlatform Platform) const; // The first line of metal -v, which gives the version const FString& GetCompilerVersionString(EShaderPlatform Platform) const; // Fully qualified path to a process-specific temporary directory on the local machine. // This directory will be destroyed when the toolchain is destroyed const FString& GetLocalTempDir() const { // nothing like dispatch_once on windows? // this should always happen on the same thread anyway static bool bLocalTempDirCreated = false; if (!bLocalTempDirCreated) { LocalTempFolder = FPaths::Combine(FPlatformProcess::UserTempDir(), TEXT("MetalShaderCompilation"), FString::Printf(TEXT("%u"), FPlatformProcess::GetCurrentProcessId())); if (!FPaths::DirectoryExists(LocalTempFolder)) { bool bSuccess = IFileManager::Get().MakeDirectory(*LocalTempFolder, true); if (!bSuccess) { UE_LOG(LogMetalCompilerSetup, Fatal, TEXT("Attempting to create temporary directory at %s but failed."), *LocalTempFolder); } } bLocalTempDirCreated = true; } return LocalTempFolder; } // The Singleton Toolchain object static const FMetalCompilerToolchain* Get() { return Singleton; } // Creates and intializes the toolchain for this process static void CreateAndInit(); // Cleans up and deletes the toolchain for this process static void Destroy(); // True if Platform indicates a mobile (ios, tvos) platform static bool IsMobile(const EShaderPlatform Platform) { return (Platform == SP_METAL_ES3_1_IOS || Platform == SP_METAL_SM5_IOS || Platform == SP_METAL_SIM || Platform == SP_METAL_ES3_1_TVOS || Platform == SP_METAL_SM5_TVOS); } // True if Format is the FName of a mobile shader format (ios, tvos) static bool IsMobile(const FName& Format) { EShaderPlatform Platform = MetalShaderFormatToLegacyShaderPlatform(Format); return IsMobile(Platform); } // True if Platform is a tvos platform static bool IsTVOS(const EShaderPlatform Platform) { return Platform == SP_METAL_ES3_1_TVOS || Platform == SP_METAL_SM5_TVOS; } // Converts an FName ShaderFormat to the equivalent ShaderPlatform static EShaderPlatform MetalShaderFormatToLegacyShaderPlatform(FName ShaderFormat); // Returns the SDK (Mac, Mobile) to use in order to compile shaders of Platform static EAppleSDKType MetalShaderPlatformToSDK(EShaderPlatform Platform) { if (IsMobile(Platform)) { return AppleSDKMobile; } return AppleSDKMac; } // Returns the SDK (Mac, Mobile) to use in order to compile shaders of ShaderFormat static EAppleSDKType MetalFormatToSDK(FName ShaderFormat) { if (IsMobile(ShaderFormat)) { return AppleSDKMobile; } return AppleSDKMac; } static const FString& SDKToString(EAppleSDKType SDK) { if(SDK == AppleSDKMac) { return MetalMacSDK; } return MetalMobileSDK; } // The extension of a metal shader - .metal static FString MetalExtention; // The extension of a packed metal library - .metallib static FString MetalLibraryExtension; // The extension of the metal IR objects - .air static FString MetalObjectExtension; // The name of the metal frontend compiler - metal static FString MetalFrontendBinary; // The name of the metal-ar archiver - metal-ar static FString MetalArBinary; // The name of the metal binary packager - metallib static FString MetalLibraryBinary; // The name of air-pack static FString AirPackBinary; // The extension of the mapping from shader to metallib for shared material libraries - .metalmap static FString MetalMapExtension; // The path to xcrun static FString XcrunPath; // The string xcrun expects for the mac SDK - macos static FString MetalMacSDK; // The string xcrun expects for the mobile SDKs - iphoneos static FString MetalMobileSDK; // The version of the windows native metal compiler static FString WindowsToolchainVersion; // The subversion needed for the windows native metal compiler after version 5.0 static FString WindowsToolchainSubversion; private: // Members bool bToolchainAvailable : 1; bool bToolchainBinariesPresent : 1; // In this implementation we will also skip PCH generation but this is left in case we'd like to turn it on in the future. // Probably needs serious testing as to whether it actually works and actually gains us anything. bool bSkipPCH : 1; // The path to metal_stdlib FString MetalStandardLibraryPath[AppleSDKCount]; // These are the strings to pass to Exec to invoke various utilities. // On Mac we'll just use xcrun and the name of the utility // On Windows, or when using an override on Mac, we will need to figure out the full path to each // The command string to invoke 'metal' FString MetalFrontendBinaryCommand[AppleSDKCount]; // The command string to invoke 'metallib' FString MetalLibBinaryCommand[AppleSDKCount]; // The command string to invoke 'metal-ar' FString MetalArBinaryCommand[AppleSDKCount]; // The command string to invoke 'air-pack' FString AirPackBinaryCommand[AppleSDKCount]; // The compiler version string, parsed out of metal -v FString MetalCompilerVersionString[AppleSDKCount]; // The compiler version number, parsed out of metal -v. This is the first number that occurs - not the number that is (metalfe-###) PackedVersion MetalCompilerVersion[AppleSDKCount]; // The compiler target version, parsed out of metal -v. This is the number from 'Target: air64-apple-darwin##.##.##' PackedVersion MetalTargetVersion[AppleSDKCount]; // Names of temporary directories, generated when the toolchain is initialized // mutable :( mutable FString LocalTempFolder; // Statics // The one and only toolchain static FMetalCompilerToolchain* Singleton; #if PLATFORM_MAC // fills out the paths and sets up the toolchain for compilation on the local mac EMetalToolchainStatus DoMacNativeSetup(); #endif #if PLATFORM_WINDOWS // Sets up toolchain for compilation on windows. EMetalToolchainStatus DoWindowsSetup(); #endif // Parses and verifies the version of the compiler. Fetched via metal -v EMetalToolchainStatus FetchCompilerVersion(); // Parses and verifies where metal_stdlib is located. EMetalToolchainStatus FetchMetalStandardLibraryPath(); }; /* */