2019-12-26 15:32:37 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-11-13 14:14:10 -05:00
// ..
2014-06-10 17:09:55 -04:00
2016-11-23 15:48:37 -05:00
# include "CoreMinimal.h"
2014-06-11 10:03:35 -04:00
# include "ShaderCore.h"
# include "MetalShaderResources.h"
2014-06-10 20:40:13 -04:00
# include "ShaderCompilerCommon.h"
2022-06-21 10:49:56 -04:00
# include "ShaderParameterParser.h"
2016-11-23 15:48:37 -05:00
# include "Misc/ConfigCacheIni.h"
# include "Serialization/MemoryWriter.h"
2017-04-11 10:32:07 -04:00
# include "Serialization/MemoryReader.h"
2016-11-23 15:48:37 -05:00
# include "Misc/FileHelper.h"
# include "Misc/Paths.h"
2022-09-20 03:10:30 -04:00
# include "Misc/EngineVersion.h"
2020-05-06 17:58:18 -04:00
# include "HAL/PlatformFileManager.h"
2014-06-10 17:09:55 -04:00
2020-09-24 00:43:27 -04:00
# include "MetalShaderFormat.h"
2014-06-10 17:09:55 -04:00
# if PLATFORM_WINDOWS
2018-01-20 11:19:29 -05:00
# include "Windows/WindowsHWrapper.h"
# include "Windows/AllowWindowsPlatformTypes.h"
2016-12-13 11:58:16 -05:00
THIRD_PARTY_INCLUDES_START
2014-06-10 17:09:55 -04:00
# include "Windows/PreWindowsApi.h"
# include <objbase.h>
# include <assert.h>
# include <stdio.h>
# include "Windows/PostWindowsApi.h"
# include "Windows/MinWindows.h"
2016-12-13 11:58:16 -05:00
THIRD_PARTY_INCLUDES_END
2018-01-20 11:19:29 -05:00
# include "Windows/HideWindowsPlatformTypes.h"
2014-06-10 17:09:55 -04:00
# endif
2016-10-20 20:09:22 -04:00
2014-06-10 17:09:55 -04:00
# include "ShaderPreprocessor.h"
2014-09-25 09:16:01 -04:00
# include "MetalBackend.h"
2017-08-24 15:38:57 -04:00
# include "MetalDerivedData.h"
2014-06-10 17:09:55 -04:00
2019-09-20 15:41:33 -04:00
# if !PLATFORM_WINDOWS
# if PLATFORM_TCHAR_IS_CHAR16
# define FP_TEXT_PASTE(x) L ## x
# define WTEXT(x) FP_TEXT_PASTE(x)
# else
# define WTEXT TEXT
# endif
# endif
2017-07-13 10:13:07 -04:00
static bool CompileProcessAllowsRuntimeShaderCompiling ( const FShaderCompilerInput & InputCompilerEnvironment )
{
2020-09-24 00:43:27 -04:00
bool bArchiving = InputCompilerEnvironment . Environment . CompilerFlags . Contains ( CFLAG_Archive ) ;
bool bDebug = InputCompilerEnvironment . Environment . CompilerFlags . Contains ( CFLAG_Debug ) ;
2017-07-13 10:13:07 -04:00
2020-09-24 00:43:27 -04:00
return ! bArchiving & & bDebug ;
2017-08-24 15:38:57 -04:00
}
2022-02-04 14:01:55 -05:00
constexpr uint16 GMetalMaxUniformBufferSlots = 32 ;
2022-09-20 03:10:30 -04:00
constexpr int32 GMetalDefaultShadingLanguageVersion = 0 ;
2022-02-04 14:01:55 -05:00
2014-06-10 17:09:55 -04:00
/*------------------------------------------------------------------------------
Shader compiling.
------------------------------------------------------------------------------*/
2015-07-02 11:52:03 -04:00
static inline uint32 ParseNumber ( const TCHAR * Str )
2014-06-10 17:09:55 -04:00
{
uint32 Num = 0 ;
while ( * Str & & * Str > = ' 0 ' & & * Str < = ' 9 ' )
{
Num = Num * 10 + * Str + + - ' 0 ' ;
}
return Num ;
}
2016-06-09 17:45:44 -04:00
static inline uint32 ParseNumber ( const ANSICHAR * Str )
{
uint32 Num = 0 ;
while ( * Str & & * Str > = ' 0 ' & & * Str < = ' 9 ' )
{
Num = Num * 10 + * Str + + - ' 0 ' ;
}
return Num ;
}
2016-12-12 17:47:42 -05:00
struct FHlslccMetalHeader : public CrossCompiler : : FHlslccHeader
{
2022-01-17 23:50:00 -05:00
FHlslccMetalHeader ( uint32 const Version ) ;
2016-12-12 17:47:42 -05:00
virtual ~ FHlslccMetalHeader ( ) ;
// After the standard header, different backends can output their own info
virtual bool ParseCustomHeaderEntries ( const ANSICHAR * & ShaderSource ) override ;
2018-11-08 20:01:54 -05:00
TMap < uint8 , TArray < uint8 > > ArgumentBuffers ;
2018-11-08 18:38:44 -05:00
int8 SideTable ;
2022-01-17 23:50:00 -05:00
uint32 Version ;
2016-12-12 17:47:42 -05:00
} ;
2022-01-17 23:50:00 -05:00
FHlslccMetalHeader : : FHlslccMetalHeader ( uint32 const InVersion )
2016-12-12 17:47:42 -05:00
{
2018-11-08 18:38:44 -05:00
SideTable = - 1 ;
2016-12-12 17:47:42 -05:00
Version = InVersion ;
}
FHlslccMetalHeader : : ~ FHlslccMetalHeader ( )
{
}
bool FHlslccMetalHeader : : ParseCustomHeaderEntries ( const ANSICHAR * & ShaderSource )
{
# define DEF_PREFIX_STR(Str) \
static const ANSICHAR* Str##Prefix = " // @" #Str ": "; \
static const int32 Str # # PrefixLen = FCStringAnsi : : Strlen ( Str # # Prefix )
2018-11-08 20:01:54 -05:00
DEF_PREFIX_STR ( ArgumentBuffers ) ;
2018-11-08 18:38:44 -05:00
DEF_PREFIX_STR ( SideTable ) ;
2016-12-12 17:47:42 -05:00
# undef DEF_PREFIX_STR
2018-11-08 18:38:44 -05:00
const ANSICHAR * SideTableString = FCStringAnsi : : Strstr ( ShaderSource , SideTablePrefix ) ;
if ( SideTableString )
{
ShaderSource = SideTableString ;
ShaderSource + = SideTablePrefixLen ;
while ( * ShaderSource & & * ShaderSource ! = ' \n ' )
{
if ( * ShaderSource = = ' ( ' )
{
ShaderSource + + ;
if ( * ShaderSource & & * ShaderSource ! = ' \n ' )
{
SideTable = ( int8 ) ParseNumber ( ShaderSource ) ;
}
}
else
{
ShaderSource + + ;
}
}
2018-12-14 11:09:25 -05:00
if ( * ShaderSource & & ! CrossCompiler : : Match ( ShaderSource , ' \n ' ) )
{
return false ;
}
2018-11-08 18:38:44 -05:00
if ( SideTable < 0 )
{
UE_LOG ( LogMetalShaderCompiler , Fatal , TEXT ( " Couldn't parse the SideTable buffer index for bounds checking " ) ) ;
return false ;
}
}
2018-11-08 20:01:54 -05:00
const ANSICHAR * ArgumentTable = FCStringAnsi : : Strstr ( ShaderSource , ArgumentBuffersPrefix ) ;
if ( ArgumentTable )
{
ShaderSource = ArgumentTable ;
ShaderSource + = ArgumentBuffersPrefixLen ;
while ( * ShaderSource & & * ShaderSource ! = ' \n ' )
{
int32 ArgumentBufferIndex = - 1 ;
if ( ! CrossCompiler : : ParseIntegerNumber ( ShaderSource , ArgumentBufferIndex ) )
{
return false ;
}
check ( ArgumentBufferIndex > = 0 ) ;
if ( ! CrossCompiler : : Match ( ShaderSource , ' [ ' ) )
{
return false ;
}
TArray < uint8 > Mask ;
while ( * ShaderSource & & * ShaderSource ! = ' ] ' )
{
int32 MaskIndex = - 1 ;
if ( ! CrossCompiler : : ParseIntegerNumber ( ShaderSource , MaskIndex ) )
{
return false ;
}
check ( MaskIndex > = 0 ) ;
Mask . Add ( ( uint8 ) MaskIndex ) ;
if ( ! CrossCompiler : : Match ( ShaderSource , ' , ' ) & & * ShaderSource ! = ' ] ' )
{
return false ;
}
}
if ( ! CrossCompiler : : Match ( ShaderSource , ' ] ' ) )
{
return false ;
}
if ( ! CrossCompiler : : Match ( ShaderSource , ' , ' ) & & * ShaderSource ! = ' \n ' )
{
return false ;
}
ArgumentBuffers . Add ( ( uint8 ) ArgumentBufferIndex , Mask ) ;
}
}
2016-12-12 17:47:42 -05:00
return true ;
}
2014-06-10 17:09:55 -04:00
/**
* Construct the final microcode from the compiled and verified shader source.
* @param ShaderOutput - Where to store the microcode and parameter map.
* @param InShaderSource - Metal source with input/output signature.
* @param SourceLen - The length of the Metal source code.
*/
2017-08-24 15:38:57 -04:00
void BuildMetalShaderOutput (
2014-06-10 17:09:55 -04:00
FShaderCompilerOutput & ShaderOutput ,
2017-08-24 15:38:57 -04:00
const FShaderCompilerInput & ShaderInput ,
FSHAHash const & GUIDHash ,
2018-09-11 14:44:10 -04:00
uint32 CCFlags ,
2014-06-10 17:09:55 -04:00
const ANSICHAR * InShaderSource ,
2017-04-11 10:32:07 -04:00
uint32 SourceLen ,
uint32 SourceCRCLen ,
uint32 SourceCRC ,
2022-01-13 12:27:13 -05:00
uint32 Version ,
2015-09-18 12:45:20 -04:00
TCHAR const * Standard ,
2017-08-24 15:38:57 -04:00
TCHAR const * MinOSVersion ,
2017-11-16 11:36:35 -05:00
EMetalTypeBufferMode TypeMode ,
2016-12-12 17:47:42 -05:00
TArray < FShaderCompilerError > & OutErrors ,
2017-11-16 11:36:35 -05:00
uint32 TypedBuffers ,
uint32 InvariantBuffers ,
uint32 TypedUAVs ,
2018-05-30 17:50:16 -04:00
uint32 ConstantBuffers ,
2017-11-16 11:36:35 -05:00
TArray < uint8 > const & TypedBufferFormats ,
2017-08-24 15:38:57 -04:00
bool bAllowFastIntriniscs
2014-06-10 17:09:55 -04:00
)
{
2017-08-24 15:38:57 -04:00
ShaderOutput . bSucceeded = false ;
2015-07-02 11:52:03 -04:00
const ANSICHAR * USFSource = InShaderSource ;
2016-12-12 17:47:42 -05:00
2018-05-01 10:36:33 -04:00
uint32 NumLines = 0 ;
const ANSICHAR * Main = FCStringAnsi : : Strstr ( USFSource , " Main_ " ) ;
while ( Main & & * Main )
{
if ( * Main = = ' \n ' )
{
NumLines + + ;
}
Main + + ;
}
2021-03-25 16:56:00 -04:00
FHlslccMetalHeader CCHeader ( Version ) ;
2015-07-02 11:52:03 -04:00
if ( ! CCHeader . Read ( USFSource , SourceLen ) )
{
UE_LOG ( LogMetalShaderCompiler , Fatal , TEXT ( " Bad hlslcc header found " ) ) ;
}
2015-09-18 12:45:20 -04:00
2021-11-18 14:37:34 -05:00
const EShaderFrequency Frequency = ShaderOutput . Target . GetFrequency ( ) ;
2020-09-24 00:43:27 -04:00
//TODO read from toolchain
2018-10-18 16:47:22 -04:00
const bool bIsMobile = ( ShaderInput . Target . Platform = = SP_METAL | | ShaderInput . Target . Platform = = SP_METAL_MRT | | ShaderInput . Target . Platform = = SP_METAL_TVOS | | ShaderInput . Target . Platform = = SP_METAL_MRT_TVOS ) ;
2017-12-15 12:47:47 -05:00
bool bNoFastMath = ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_NoFastMath ) ;
FString const * UsingWPO = ShaderInput . Environment . GetDefinitions ( ) . Find ( TEXT ( " USES_WORLD_POSITION_OFFSET " ) ) ;
2018-10-18 16:47:22 -04:00
if ( UsingWPO & & FString ( " 1 " ) = = * UsingWPO & & ( ShaderInput . Target . Platform = = SP_METAL_MRT | | ShaderInput . Target . Platform = = SP_METAL_MRT_TVOS ) & & Frequency = = SF_Vertex )
2017-12-15 12:47:47 -05:00
{
// WPO requires that we make all multiply/sincos instructions invariant :(
bNoFastMath = true ;
}
2020-09-24 00:43:27 -04:00
2018-09-11 14:44:10 -04:00
FMetalCodeHeader Header ;
2017-04-11 10:32:07 -04:00
Header . CompileFlags = ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Debug ) ? ( 1 < < CFLAG_Debug ) : 0 ) ;
2017-12-15 12:47:47 -05:00
Header . CompileFlags | = ( bNoFastMath ? ( 1 < < CFLAG_NoFastMath ) : 0 ) ;
2021-08-16 23:18:23 -04:00
Header . CompileFlags | = ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_ExtraShaderData ) ? ( 1 < < CFLAG_ExtraShaderData ) : 0 ) ;
2017-04-11 10:32:07 -04:00
Header . CompileFlags | = ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_ZeroInitialise ) ? ( 1 < < CFLAG_ZeroInitialise ) : 0 ) ;
Header . CompileFlags | = ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_BoundsChecking ) ? ( 1 < < CFLAG_BoundsChecking ) : 0 ) ;
Header . CompileFlags | = ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Archive ) ? ( 1 < < CFLAG_Archive ) : 0 ) ;
2020-09-24 00:43:27 -04:00
Header . CompilerVersion = FMetalCompilerToolchain : : Get ( ) - > GetCompilerVersion ( ( EShaderPlatform ) ShaderInput . Target . Platform ) . Version ;
Header . CompilerBuild = FMetalCompilerToolchain : : Get ( ) - > GetTargetVersion ( ( EShaderPlatform ) ShaderInput . Target . Platform ) . Version ;
2016-12-12 17:47:42 -05:00
Header . Version = Version ;
2016-06-09 17:45:44 -04:00
Header . SideTable = - 1 ;
2017-04-11 10:32:07 -04:00
Header . SourceLen = SourceCRCLen ;
Header . SourceCRC = SourceCRC ;
2022-01-13 12:27:13 -05:00
Header . Bindings . bDiscards = false ;
2018-05-30 17:50:16 -04:00
Header . Bindings . ConstantBuffers = ConstantBuffers ;
2017-11-16 11:36:35 -05:00
{
Header . Bindings . TypedBuffers = TypedBuffers ;
for ( uint32 i = 0 ; i < ( uint32 ) TypedBufferFormats . Num ( ) ; i + + )
{
if ( ( TypedBuffers & ( 1 < < i ) ) ! = 0 )
{
2019-10-16 13:04:00 -04:00
check ( TypedBufferFormats [ i ] > ( uint8 ) EMetalBufferFormat : : Unknown ) ;
2022-01-13 12:27:13 -05:00
check ( TypedBufferFormats [ i ] < ( uint8 ) EMetalBufferFormat : : Max ) ;
if ( ( TypeMode > EMetalTypeBufferModeRaw )
& & ( TypeMode < = EMetalTypeBufferModeTB )
& & ( TypedBufferFormats [ i ] < ( uint8 ) EMetalBufferFormat : : RGB8Sint | | TypedBufferFormats [ i ] > ( uint8 ) EMetalBufferFormat : : RGB32Float )
& & ( TypeMode = = EMetalTypeBufferMode2D | | TypeMode = = EMetalTypeBufferModeTB | | ! ( TypedUAVs & ( 1 < < i ) ) ) )
{
Header . Bindings . LinearBuffer | = ( 1 < < i ) ;
Header . Bindings . TypedBuffers & = ~ ( 1 < < i ) ;
}
2017-11-16 11:36:35 -05:00
}
}
2020-09-24 00:43:27 -04:00
Header . Bindings . LinearBuffer = Header . Bindings . TypedBuffers ;
Header . Bindings . TypedBuffers = 0 ;
2019-06-11 18:27:07 -04:00
2017-11-16 11:36:35 -05:00
// Raw mode means all buffers are invariant
if ( TypeMode = = EMetalTypeBufferModeRaw )
{
Header . Bindings . TypedBuffers = 0 ;
}
}
2017-04-11 10:32:07 -04:00
2014-06-10 17:09:55 -04:00
FShaderParameterMap & ParameterMap = ShaderOutput . ParameterMap ;
2014-06-10 20:40:13 -04:00
TBitArray < > UsedUniformBufferSlots ;
UsedUniformBufferSlots . Init ( false , 32 ) ;
2014-06-10 17:09:55 -04:00
// Write out the magic markers.
Header . Frequency = Frequency ;
2015-07-02 11:52:03 -04:00
// Only inputs for vertex shaders must be tracked.
if ( Frequency = = SF_Vertex )
2015-06-24 17:40:55 -04:00
{
2015-07-02 11:52:03 -04:00
static const FString AttributePrefix = TEXT ( " in_ATTRIBUTE " ) ;
for ( auto & Input : CCHeader . Inputs )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
// Only process attributes.
if ( Input . Name . StartsWith ( AttributePrefix ) )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
uint8 AttributeIndex = ParseNumber ( * Input . Name + AttributePrefix . Len ( ) ) ;
2021-03-08 13:24:59 -04:00
Header . Bindings . InOutMask . EnableField ( AttributeIndex ) ;
2014-06-10 17:09:55 -04:00
}
}
}
// Then the list of outputs.
2017-08-24 15:38:57 -04:00
static const FString TargetPrefix = " FragColor " ;
2019-06-11 18:27:07 -04:00
static const FString TargetPrefix2 = " SV_Target " ;
2015-07-02 11:52:03 -04:00
// Only outputs for pixel shaders must be tracked.
if ( Frequency = = SF_Pixel )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
for ( auto & Output : CCHeader . Outputs )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
// Handle targets.
if ( Output . Name . StartsWith ( TargetPrefix ) )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
uint8 TargetIndex = ParseNumber ( * Output . Name + TargetPrefix . Len ( ) ) ;
2021-03-08 13:24:59 -04:00
Header . Bindings . InOutMask . EnableField ( TargetIndex ) ;
2015-07-02 11:52:03 -04:00
}
2019-06-11 18:27:07 -04:00
else if ( Output . Name . StartsWith ( TargetPrefix2 ) )
2015-07-02 11:52:03 -04:00
{
2019-06-11 18:27:07 -04:00
uint8 TargetIndex = ParseNumber ( * Output . Name + TargetPrefix2 . Len ( ) ) ;
2021-03-08 13:24:59 -04:00
Header . Bindings . InOutMask . EnableField ( TargetIndex ) ;
2014-06-10 17:09:55 -04:00
}
2022-01-13 12:27:13 -05:00
}
2019-06-11 18:27:07 -04:00
// For fragment shaders that discard but don't output anything we need at least a depth-stencil surface, so we need a way to validate this at runtime.
if ( FCStringAnsi : : Strstr ( USFSource , " [[ depth( " ) ! = nullptr | | FCStringAnsi : : Strstr ( USFSource , " [[depth( " ) ! = nullptr )
{
2021-03-08 13:24:59 -04:00
Header . Bindings . InOutMask . EnableField ( CrossCompiler : : FShaderBindingInOutMask : : DepthStencilMaskIndex ) ;
2019-06-11 18:27:07 -04:00
}
2017-12-15 12:47:47 -05:00
2022-01-13 12:27:13 -05:00
// For fragment shaders that discard but don't output anything we need at least a depth-stencil surface, so we need a way to validate this at runtime.
if ( FCStringAnsi : : Strstr ( USFSource , " discard_fragment() " ) ! = nullptr )
{
Header . Bindings . bDiscards = true ;
}
2014-06-10 17:09:55 -04:00
}
// Then 'normal' uniform buffers.
2022-02-04 14:01:55 -05:00
bool bOutOfBounds = false ;
2015-07-02 11:52:03 -04:00
for ( auto & UniformBlock : CCHeader . UniformBlocks )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
uint16 UBIndex = UniformBlock . Index ;
if ( UBIndex > = Header . Bindings . NumUniformBuffers )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
Header . Bindings . NumUniformBuffers = UBIndex + 1 ;
2014-06-10 17:09:55 -04:00
}
2022-02-04 14:01:55 -05:00
if ( UBIndex > = GMetalMaxUniformBufferSlots )
{
bOutOfBounds = true ;
new ( OutErrors ) FShaderCompilerError ( * FString : : Printf ( TEXT ( " Uniform buffer index (%d) exceeded upper bound of slots (%d) for Metal API: %s " ) , UBIndex , GMetalMaxUniformBufferSlots , * UniformBlock . Name ) ) ;
continue ;
}
2015-07-02 11:52:03 -04:00
UsedUniformBufferSlots [ UBIndex ] = true ;
2022-05-03 11:21:15 -04:00
HandleReflectedUniformBuffer ( UniformBlock . Name , UBIndex , ShaderOutput ) ;
2014-06-10 17:09:55 -04:00
}
2022-02-04 14:01:55 -05:00
if ( bOutOfBounds )
{
ShaderOutput . bSucceeded = false ;
return ;
}
2014-06-10 17:09:55 -04:00
// Packed global uniforms
2015-07-02 11:52:03 -04:00
const uint16 BytesPerComponent = 4 ;
2014-06-10 17:09:55 -04:00
TMap < ANSICHAR , uint16 > PackedGlobalArraySize ;
2015-07-02 11:52:03 -04:00
for ( auto & PackedGlobal : CCHeader . PackedGlobals )
2014-06-10 17:09:55 -04:00
{
2022-05-03 11:21:15 -04:00
HandleReflectedGlobalConstantBufferMember (
PackedGlobal . Name ,
2015-07-02 11:52:03 -04:00
PackedGlobal . PackedType ,
PackedGlobal . Offset * BytesPerComponent ,
2018-12-11 22:25:04 -05:00
PackedGlobal . Count * BytesPerComponent ,
2022-05-03 11:21:15 -04:00
ShaderOutput
2022-01-13 12:27:13 -05:00
) ;
2014-06-10 17:09:55 -04:00
2015-07-02 11:52:03 -04:00
uint16 & Size = PackedGlobalArraySize . FindOrAdd ( PackedGlobal . PackedType ) ;
Size = FMath : : Max < uint16 > ( BytesPerComponent * ( PackedGlobal . Offset + PackedGlobal . Count ) , Size ) ;
2014-06-10 17:09:55 -04:00
}
// Packed Uniform Buffers
2020-09-24 00:43:27 -04:00
TMap < int , TMap < CrossCompiler : : EPackedTypeName , uint16 > > PackedUniformBuffersSize ;
2018-09-17 18:35:24 -04:00
for ( auto & PackedUB : CCHeader . PackedUBs )
2014-06-10 17:09:55 -04:00
{
2018-09-17 18:35:24 -04:00
for ( auto & Member : PackedUB . Members )
2018-09-11 14:44:10 -04:00
{
2022-05-03 11:21:15 -04:00
HandleReflectedGlobalConstantBufferMember (
Member . Name ,
( uint32 ) CrossCompiler : : EPackedTypeName : : HighP ,
2022-01-13 12:27:13 -05:00
Member . Offset * BytesPerComponent ,
2022-05-03 11:21:15 -04:00
Member . Count * BytesPerComponent ,
ShaderOutput
2022-01-13 12:27:13 -05:00
) ;
2018-09-11 14:44:10 -04:00
2020-09-24 00:43:27 -04:00
uint16 & Size = PackedUniformBuffersSize . FindOrAdd ( PackedUB . Attribute . Index ) . FindOrAdd ( CrossCompiler : : EPackedTypeName : : HighP ) ;
2018-09-17 18:35:24 -04:00
Size = FMath : : Max < uint16 > ( BytesPerComponent * ( Member . Offset + Member . Count ) , Size ) ;
2018-09-11 14:44:10 -04:00
}
2014-06-10 17:09:55 -04:00
}
// Setup Packed Array info
Header . Bindings . PackedGlobalArrays . Reserve ( PackedGlobalArraySize . Num ( ) ) ;
for ( auto Iterator = PackedGlobalArraySize . CreateIterator ( ) ; Iterator ; + + Iterator )
{
ANSICHAR TypeName = Iterator . Key ( ) ;
uint16 Size = Iterator . Value ( ) ;
Size = ( Size + 0xf ) & ( ~ 0xf ) ;
2014-07-15 13:25:14 -04:00
CrossCompiler : : FPackedArrayInfo Info ;
2014-06-10 17:09:55 -04:00
Info . Size = Size ;
Info . TypeName = TypeName ;
2018-09-11 14:44:10 -04:00
Info . TypeIndex = ( uint8 ) CrossCompiler : : PackedTypeNameToTypeIndex ( TypeName ) ;
2014-06-10 17:09:55 -04:00
Header . Bindings . PackedGlobalArrays . Add ( Info ) ;
}
// Setup Packed Uniform Buffers info
Header . Bindings . PackedUniformBuffers . Reserve ( PackedUniformBuffersSize . Num ( ) ) ;
2018-09-11 14:44:10 -04:00
2018-09-17 18:35:24 -04:00
// In this mode there should only be 0 or 1 packed UB that contains all the aligned & named global uniform parameters
check ( PackedUniformBuffersSize . Num ( ) < = 1 ) ;
for ( auto Iterator = PackedUniformBuffersSize . CreateIterator ( ) ; Iterator ; + + Iterator )
2014-06-10 17:09:55 -04:00
{
2018-09-17 18:35:24 -04:00
int BufferIndex = Iterator . Key ( ) ;
auto & ArraySizes = Iterator . Value ( ) ;
for ( auto IterSizes = ArraySizes . CreateIterator ( ) ; IterSizes ; + + IterSizes )
2014-06-10 17:09:55 -04:00
{
2020-09-24 00:43:27 -04:00
CrossCompiler : : EPackedTypeName TypeName = IterSizes . Key ( ) ;
2018-09-17 18:35:24 -04:00
uint16 Size = IterSizes . Value ( ) ;
Size = ( Size + 0xf ) & ( ~ 0xf ) ;
CrossCompiler : : FPackedArrayInfo Info ;
Info . Size = Size ;
2020-09-24 00:43:27 -04:00
Info . TypeName = ( ANSICHAR ) TypeName ;
2018-09-17 18:35:24 -04:00
Info . TypeIndex = BufferIndex ;
Header . Bindings . PackedGlobalArrays . Add ( Info ) ;
2014-06-10 17:09:55 -04:00
}
}
2017-03-21 17:46:52 -04:00
uint32 NumTextures = 0 ;
// Then samplers.
2015-06-24 17:40:55 -04:00
TMap < FString , uint32 > SamplerMap ;
2015-07-02 11:52:03 -04:00
for ( auto & Sampler : CCHeader . Samplers )
2014-06-10 17:09:55 -04:00
{
2022-05-03 11:21:15 -04:00
HandleReflectedShaderResource ( Sampler . Name , Sampler . Offset , Sampler . Count , ShaderOutput ) ;
2014-06-10 17:09:55 -04:00
2017-03-21 17:46:52 -04:00
NumTextures + = Sampler . Count ;
2015-07-02 11:52:03 -04:00
for ( auto & SamplerState : Sampler . SamplerStates )
2014-06-10 17:09:55 -04:00
{
2015-07-02 11:52:03 -04:00
SamplerMap . Add ( SamplerState , Sampler . Count ) ;
2014-06-10 17:09:55 -04:00
}
2017-03-21 17:46:52 -04:00
}
Header . Bindings . NumSamplers = CCHeader . SamplerStates . Num ( ) ;
2014-06-10 17:09:55 -04:00
// Then UAVs (images in Metal)
2015-07-02 11:52:03 -04:00
for ( auto & UAV : CCHeader . UAVs )
2014-06-10 17:09:55 -04:00
{
2022-05-03 11:21:15 -04:00
HandleReflectedShaderUAV ( UAV . Name , UAV . Offset , UAV . Count , ShaderOutput ) ;
2014-06-10 17:09:55 -04:00
2015-07-02 11:52:03 -04:00
Header . Bindings . NumUAVs = FMath : : Max < uint8 > (
Header . Bindings . NumSamplers ,
UAV . Offset + UAV . Count
) ;
2015-04-16 17:03:26 -04:00
}
2015-07-02 11:52:03 -04:00
for ( auto & SamplerState : CCHeader . SamplerStates )
2015-04-16 17:03:26 -04:00
{
2018-11-08 20:01:54 -05:00
if ( ! SamplerMap . Contains ( SamplerState . Name ) )
{
SamplerMap . Add ( SamplerState . Name , 1 ) ;
}
2022-05-17 13:36:05 -04:00
HandleReflectedShaderSampler ( SamplerState . Name , SamplerState . Index , SamplerMap [ SamplerState . Name ] , ShaderOutput ) ;
2014-06-10 17:09:55 -04:00
}
2015-07-02 11:52:03 -04:00
Header . NumThreadsX = CCHeader . NumThreads [ 0 ] ;
Header . NumThreadsY = CCHeader . NumThreads [ 1 ] ;
Header . NumThreadsZ = CCHeader . NumThreads [ 2 ] ;
2016-12-12 17:47:42 -05:00
2022-01-13 12:27:13 -05:00
Header . bDeviceFunctionConstants = ( FCStringAnsi : : Strstr ( USFSource , " #define __METAL_DEVICE_CONSTANT_INDEX__ 1 " ) ! = nullptr ) ;
Header . SideTable = CCHeader . SideTable ;
Header . Bindings . ArgumentBufferMasks = CCHeader . ArgumentBuffers ;
Header . Bindings . ArgumentBuffers = 0 ;
2018-11-08 20:01:54 -05:00
for ( auto const & Pair : Header . Bindings . ArgumentBufferMasks )
{
Header . Bindings . ArgumentBuffers | = ( 1 < < Pair . Key ) ;
}
2016-12-12 17:47:42 -05:00
2014-06-10 17:14:51 -04:00
// Build the SRT for this shader.
{
// Build the generic SRT for this shader.
2015-10-07 17:46:58 -04:00
FShaderCompilerResourceTable GenericSRT ;
2020-12-07 17:42:32 -04:00
BuildResourceTableMapping ( ShaderInput . Environment . ResourceTableMap , ShaderInput . Environment . UniformBufferMap , UsedUniformBufferSlots , ShaderOutput . ParameterMap , GenericSRT ) ;
CullGlobalUniformBuffers ( ShaderInput . Environment . UniformBufferMap , ShaderOutput . ParameterMap ) ;
2014-06-10 17:14:51 -04:00
// Copy over the bits indicating which resource tables are active.
Header . Bindings . ShaderResourceTable . ResourceTableBits = GenericSRT . ResourceTableBits ;
Header . Bindings . ShaderResourceTable . ResourceTableLayoutHashes = GenericSRT . ResourceTableLayoutHashes ;
// Now build our token streams.
BuildResourceTableTokenStream ( GenericSRT . TextureMap , GenericSRT . MaxBoundResourceTable , Header . Bindings . ShaderResourceTable . TextureMap ) ;
BuildResourceTableTokenStream ( GenericSRT . ShaderResourceViewMap , GenericSRT . MaxBoundResourceTable , Header . Bindings . ShaderResourceTable . ShaderResourceViewMap ) ;
BuildResourceTableTokenStream ( GenericSRT . SamplerMap , GenericSRT . MaxBoundResourceTable , Header . Bindings . ShaderResourceTable . SamplerMap ) ;
BuildResourceTableTokenStream ( GenericSRT . UnorderedAccessViewMap , GenericSRT . MaxBoundResourceTable , Header . Bindings . ShaderResourceTable . UnorderedAccessViewMap ) ;
2014-09-12 17:21:49 -04:00
Header . Bindings . NumUniformBuffers = FMath : : Max ( ( uint8 ) GetNumUniformBuffersUsed ( GenericSRT ) , Header . Bindings . NumUniformBuffers ) ;
2014-06-10 17:14:51 -04:00
}
2016-07-12 15:06:08 -04:00
FString MetalCode = FString ( USFSource ) ;
2021-11-18 14:37:34 -05:00
if ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_ExtraShaderData ) )
{
ShaderOutput . ShaderCode . AddOptionalData ( FShaderCodeName : : Key , TCHAR_TO_UTF8 ( * ShaderInput . GenerateShaderName ( ) ) ) ;
}
if ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Debug ) )
2016-07-12 15:06:08 -04:00
{
MetalCode . InsertAt ( 0 , FString : : Printf ( TEXT ( " // %s \n " ) , * CCHeader . Name ) ) ;
}
2017-08-24 15:38:57 -04:00
if ( Header . Bindings . NumSamplers > MaxMetalSamplers )
2014-06-10 17:09:55 -04:00
{
ShaderOutput . bSucceeded = false ;
2022-02-04 14:01:55 -05:00
FShaderCompilerError * NewError = new ( OutErrors ) FShaderCompilerError ( ) ;
2017-08-24 15:38:57 -04:00
FString SamplerList ;
for ( int32 i = 0 ; i < CCHeader . SamplerStates . Num ( ) ; i + + )
{
auto const & Sampler = CCHeader . SamplerStates [ i ] ;
SamplerList + = FString : : Printf ( TEXT ( " %d:%s \n " ) , Sampler . Index , * Sampler . Name ) ;
}
2014-06-10 17:09:55 -04:00
NewError - > StrippedErrorMessage =
2017-08-24 15:38:57 -04:00
FString : : Printf ( TEXT ( " shader uses %d (%d) samplers exceeding the limit of %d \n Samplers: \n %s " ) ,
Header . Bindings . NumSamplers , CCHeader . SamplerStates . Num ( ) , MaxMetalSamplers , * SamplerList ) ;
2014-06-10 17:09:55 -04:00
}
2020-09-24 00:43:27 -04:00
// TODO read from toolchain? this check isn't really doing exactly what it says
2017-12-15 12:47:47 -05:00
else if ( CompileProcessAllowsRuntimeShaderCompiling ( ShaderInput ) )
2015-06-24 17:40:55 -04:00
{
// Write out the header and shader source code.
2015-09-25 17:34:00 -04:00
FMemoryWriter Ar ( ShaderOutput . ShaderCode . GetWriteAccess ( ) , true ) ;
2015-06-24 17:40:55 -04:00
uint8 PrecompiledFlag = 0 ;
Ar < < PrecompiledFlag ;
Ar < < Header ;
2015-07-02 11:52:03 -04:00
Ar . Serialize ( ( void * ) USFSource , SourceLen + 1 - ( USFSource - InShaderSource ) ) ;
2015-06-24 17:40:55 -04:00
2018-05-23 21:04:31 -04:00
if ( ShaderInput . ExtraSettings . bExtractShaderSource )
{
ShaderOutput . OptionalFinalShaderSource = MetalCode ;
}
2018-05-01 10:36:33 -04:00
ShaderOutput . NumInstructions = NumLines ;
2015-06-24 17:40:55 -04:00
ShaderOutput . NumTextureSamplers = Header . Bindings . NumSamplers ;
ShaderOutput . bSucceeded = true ;
}
2014-06-10 17:09:55 -04:00
else
{
2020-09-24 00:43:27 -04:00
// TODO technically should probably check the version of the metal compiler to make sure it's recent enough to support -MO.
2022-01-13 12:27:13 -05:00
FString DebugInfo = TEXT ( " " ) ;
2021-08-16 23:18:23 -04:00
if ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_ExtraShaderData ) )
2018-09-11 14:44:10 -04:00
{
2020-10-22 19:19:16 -04:00
DebugInfo = TEXT ( " -gline-tables-only -MO " ) ;
2018-09-11 14:44:10 -04:00
}
2022-01-13 12:27:13 -05:00
FString MathMode = bNoFastMath ? TEXT ( " -fno-fast-math " ) : TEXT ( " -ffast-math " ) ;
2017-08-24 15:38:57 -04:00
2014-06-10 17:09:55 -04:00
// at this point, the shader source is ready to be compiled
2020-09-24 00:43:27 -04:00
// we will need a working temp directory.
const FString & TempDir = FMetalCompilerToolchain : : Get ( ) - > GetLocalTempDir ( ) ;
2014-06-10 17:09:55 -04:00
int32 ReturnCode = 0 ;
FString Results ;
FString Errors ;
2015-06-24 17:40:55 -04:00
bool bSucceeded = false ;
2014-06-10 17:09:55 -04:00
2020-01-24 18:07:01 -05:00
EShaderPlatform ShaderPlatform = EShaderPlatform ( ShaderInput . Target . Platform ) ;
2020-09-24 00:43:27 -04:00
const bool bMetalCompilerAvailable = FMetalCompilerToolchain : : Get ( ) - > IsCompilerAvailable ( ) ;
2018-09-11 14:44:10 -04:00
bool bDebugInfoSucceded = false ;
FMetalShaderBytecode Bytecode ;
FMetalShaderDebugInfo DebugCode ;
FString HashedName = FString : : Printf ( TEXT ( " %u_%u " ) , SourceCRCLen , SourceCRC ) ;
if ( ! bMetalCompilerAvailable )
2017-09-25 14:08:25 -04:00
{
2018-09-11 14:44:10 -04:00
// No Metal Compiler - just put the source code directly into /tmp and report error - we are now using text shaders when this was not the requested configuration
// Move it into place using an atomic move - ensures only one compile "wins"
2020-09-24 00:43:27 -04:00
FString InputFilename = ( TempDir / HashedName ) + FMetalCompilerToolchain : : MetalExtention ;
2018-09-11 14:44:10 -04:00
FString SaveFile = FPaths : : CreateTempFilename ( * TempDir , TEXT ( " ShaderTemp " ) , TEXT ( " " ) ) ;
FFileHelper : : SaveStringToFile ( MetalCode , * SaveFile ) ;
IFileManager : : Get ( ) . Move ( * InputFilename , * SaveFile , false , false , true , true ) ;
IFileManager : : Get ( ) . Delete ( * SaveFile ) ;
2017-09-25 14:08:25 -04:00
TCHAR const * Message = nullptr ;
2020-09-24 00:43:27 -04:00
if ( PLATFORM_MAC )
2017-09-25 14:08:25 -04:00
{
Message = TEXT ( " Xcode's metal shader compiler was not found, verify Xcode has been installed on this Mac and that it has been selected in Xcode > Preferences > Locations > Command-line Tools. " ) ;
}
2018-09-11 14:44:10 -04:00
2017-09-25 14:08:25 -04:00
FShaderCompilerError * Error = new ( OutErrors ) FShaderCompilerError ( ) ;
Error - > ErrorVirtualFilePath = InputFilename ;
Error - > ErrorLineString = TEXT ( " 0 " ) ;
Error - > StrippedErrorMessage = FString ( Message ) ;
}
2018-09-11 14:44:10 -04:00
else
2017-04-11 10:32:07 -04:00
{
2020-09-24 00:43:27 -04:00
// Compiler available - more intermediate files will be created.
// TODO How to handle multiple streams on the same machine? Needs more uniqueness in the temp dir
const FString & CompilerVersionString = FMetalCompilerToolchain : : Get ( ) - > GetCompilerVersionString ( ShaderPlatform ) ;
2017-04-11 10:32:07 -04:00
2020-09-24 00:43:27 -04:00
// The base name (which is <temp>/CRCHash_Length)
FString BaseFileName = FPaths : : Combine ( TempDir , HashedName ) ;
// The actual metal shadertext, a .metal file
FString MetalFileName = BaseFileName + FMetalCompilerToolchain : : MetalExtention ;
// The compiled shader, as AIR. An .air file.
FString AIRFileName = BaseFileName + FMetalCompilerToolchain : : MetalObjectExtension ;
// A metallib containing just this shader (gross). A .metallib file.
FString MetallibFileName = BaseFileName + FMetalCompilerToolchain : : MetalLibraryExtension ;
// 'MetalCode' contains the cross compiled MetalSL version of the shader.
// Save it out.
2017-11-16 11:36:35 -05:00
{
2020-09-24 00:43:27 -04:00
// This is the previous behavior, which attempts an atomic move in case there is a race here.
// TODO can we ever actually race on this? TempDir should be process specific now.
FString SaveFile = FPaths : : CreateTempFilename ( * TempDir , TEXT ( " ShaderTemp " ) , TEXT ( " " ) ) ;
bool bSuccess = FFileHelper : : SaveStringToFile ( MetalCode , * SaveFile ) ;
if ( ! bSuccess )
2017-11-16 11:36:35 -05:00
{
2020-09-24 00:43:27 -04:00
UE_LOG ( LogMetalShaderCompiler , Fatal , TEXT ( " Failed to write Metal shader out to %s \n ShaderText: \n %s " ) , * SaveFile , * MetalCode ) ;
2017-11-16 11:36:35 -05:00
}
2020-09-24 00:43:27 -04:00
bSuccess = IFileManager : : Get ( ) . Move ( * MetalFileName , * SaveFile , false , false , true , true ) ;
2021-07-22 11:11:51 -04:00
if ( ! bSuccess & & ! FPaths : : FileExists ( MetalFileName ) )
2017-11-16 11:36:35 -05:00
{
2020-09-24 00:43:27 -04:00
UE_LOG ( LogMetalShaderCompiler , Fatal , TEXT ( " Failed to move %s to %s " ) , * SaveFile , * MetalFileName ) ;
2017-11-16 11:36:35 -05:00
}
2020-09-24 00:43:27 -04:00
2021-07-22 11:11:51 -04:00
if ( FPaths : : FileExists ( SaveFile ) )
2017-11-16 11:36:35 -05:00
{
2020-09-24 00:43:27 -04:00
IFileManager : : Get ( ) . Delete ( * SaveFile ) ;
2017-11-16 11:36:35 -05:00
}
}
2017-08-24 15:38:57 -04:00
2020-09-24 00:43:27 -04:00
// TODO This is the actual MetalSL -> AIR piece
2017-11-16 11:36:35 -05:00
FMetalShaderBytecodeJob Job ;
2020-09-24 00:43:27 -04:00
Job . IncludeDir = TempDir ;
2017-08-24 15:38:57 -04:00
Job . ShaderFormat = ShaderInput . ShaderFormat ;
Job . Hash = GUIDHash ;
Job . TmpFolder = TempDir ;
2020-09-24 00:43:27 -04:00
Job . InputFile = MetalFileName ;
Job . OutputFile = MetallibFileName ;
Job . OutputObjectFile = AIRFileName ;
Job . CompilerVersion = CompilerVersionString ;
2017-08-24 15:38:57 -04:00
Job . MinOSVersion = MinOSVersion ;
Job . DebugInfo = DebugInfo ;
Job . MathMode = MathMode ;
Job . Standard = Standard ;
Job . SourceCRCLen = SourceCRCLen ;
Job . SourceCRC = SourceCRC ;
Job . bRetainObjectFile = ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Archive ) ;
Job . bCompileAsPCH = false ;
2021-02-11 17:43:48 -04:00
Job . ReturnCode = 0 ;
2017-11-22 16:42:04 -05:00
2021-11-18 14:37:34 -05:00
bSucceeded = FMetalCompilerToolchain : : Get ( ) - > CompileMetalShader ( Job , Bytecode ) ;
2017-08-24 15:38:57 -04:00
if ( bSucceeded )
{
2021-11-18 14:37:34 -05:00
if ( ! bIsMobile & & ! ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Archive ) )
2016-09-09 20:13:41 -04:00
{
2021-11-18 14:37:34 -05:00
uint32 CodeSize = FCStringAnsi : : Strlen ( TCHAR_TO_UTF8 ( * MetalCode ) ) + 1 ;
int32 CompressedSize = FCompression : : CompressMemoryBound ( NAME_Zlib , CodeSize ) ;
DebugCode . CompressedData . SetNum ( CompressedSize ) ;
if ( FCompression : : CompressMemory ( NAME_Zlib , DebugCode . CompressedData . GetData ( ) , CompressedSize , TCHAR_TO_UTF8 ( * MetalCode ) , CodeSize ) )
2017-08-24 15:38:57 -04:00
{
2021-11-18 14:37:34 -05:00
DebugCode . UncompressedSize = CodeSize ;
DebugCode . CompressedData . SetNum ( CompressedSize ) ;
DebugCode . CompressedData . Shrink ( ) ;
2017-08-24 15:38:57 -04:00
}
2017-04-11 10:32:07 -04:00
}
2016-09-09 20:13:41 -04:00
}
2017-07-13 10:13:07 -04:00
else
{
FShaderCompilerError * Error = new ( OutErrors ) FShaderCompilerError ( ) ;
2020-09-24 00:43:27 -04:00
Error - > ErrorVirtualFilePath = MetalFileName ;
2017-07-13 10:13:07 -04:00
Error - > ErrorLineString = TEXT ( " 0 " ) ;
2017-08-24 15:38:57 -04:00
Error - > StrippedErrorMessage = Job . Message ;
2017-07-13 10:13:07 -04:00
}
2016-09-09 20:13:41 -04:00
}
2017-07-13 10:13:07 -04:00
2017-08-24 15:38:57 -04:00
if ( bSucceeded )
2014-06-10 17:09:55 -04:00
{
2017-08-24 15:38:57 -04:00
// Write out the header and compiled shader code
FMemoryWriter Ar ( ShaderOutput . ShaderCode . GetWriteAccess ( ) , true ) ;
2017-12-15 12:47:47 -05:00
uint8 PrecompiledFlag = 1 ;
2017-08-24 15:38:57 -04:00
Ar < < PrecompiledFlag ;
2017-12-15 12:47:47 -05:00
Ar < < Header ;
2017-04-11 10:32:07 -04:00
2017-12-15 12:47:47 -05:00
// jam it into the output bytes
Ar . Serialize ( Bytecode . OutputFile . GetData ( ) , Bytecode . OutputFile . Num ( ) ) ;
if ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Archive ) )
2017-04-11 10:32:07 -04:00
{
2017-12-15 12:47:47 -05:00
ShaderOutput . ShaderCode . AddOptionalData ( ' o ' , Bytecode . ObjectFile . GetData ( ) , Bytecode . ObjectFile . Num ( ) ) ;
2017-08-24 15:38:57 -04:00
}
2015-09-25 17:34:00 -04:00
2017-12-15 12:47:47 -05:00
if ( bDebugInfoSucceded & & ! ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Archive ) & & DebugCode . CompressedData . Num ( ) )
2017-08-24 15:38:57 -04:00
{
ShaderOutput . ShaderCode . AddOptionalData ( ' z ' , DebugCode . CompressedData . GetData ( ) , DebugCode . CompressedData . Num ( ) ) ;
ShaderOutput . ShaderCode . AddOptionalData ( ' p ' , TCHAR_TO_UTF8 ( * Bytecode . NativePath ) ) ;
ShaderOutput . ShaderCode . AddOptionalData ( ' u ' , ( const uint8 * ) & DebugCode . UncompressedSize , sizeof ( DebugCode . UncompressedSize ) ) ;
}
2017-07-21 21:01:33 -04:00
2021-08-16 23:18:23 -04:00
if ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_ExtraShaderData ) )
2017-08-24 15:38:57 -04:00
{
2020-12-08 16:54:51 -04:00
// store data we can pickup later with ShaderCode.FindOptionalData(FShaderCodeName::Key), could be removed for shipping
ShaderOutput . ShaderCode . AddOptionalData ( FShaderCodeName : : Key , TCHAR_TO_UTF8 ( * ShaderInput . GenerateShaderName ( ) ) ) ;
2017-08-24 15:38:57 -04:00
if ( DebugCode . CompressedData . Num ( ) = = 0 )
{
ShaderOutput . ShaderCode . AddOptionalData ( ' c ' , TCHAR_TO_UTF8 ( * MetalCode ) ) ;
ShaderOutput . ShaderCode . AddOptionalData ( ' p ' , TCHAR_TO_UTF8 ( * Bytecode . NativePath ) ) ;
}
}
else if ( ShaderInput . Environment . CompilerFlags . Contains ( CFLAG_Archive ) )
{
ShaderOutput . ShaderCode . AddOptionalData ( ' c ' , TCHAR_TO_UTF8 ( * MetalCode ) ) ;
ShaderOutput . ShaderCode . AddOptionalData ( ' p ' , TCHAR_TO_UTF8 ( * Bytecode . NativePath ) ) ;
}
ShaderOutput . NumTextureSamplers = Header . Bindings . NumSamplers ;
2017-04-11 10:32:07 -04:00
}
2018-05-23 21:04:31 -04:00
if ( ShaderInput . ExtraSettings . bExtractShaderSource )
{
ShaderOutput . OptionalFinalShaderSource = MetalCode ;
}
2016-12-12 17:47:42 -05:00
2018-05-01 10:36:33 -04:00
ShaderOutput . NumInstructions = NumLines ;
2017-04-11 10:32:07 -04:00
ShaderOutput . bSucceeded = bSucceeded ;
2014-06-10 17:09:55 -04:00
}
}
/*------------------------------------------------------------------------------
External interface.
------------------------------------------------------------------------------*/
2017-10-24 10:14:07 -04:00
FString CreateRemoteDataFromEnvironment ( const FShaderCompilerEnvironment & Environment )
{
FString Line = TEXT ( " \n #if 0 /*BEGIN_REMOTE_SERVER*/ \n " ) ;
for ( auto Pair : Environment . RemoteServerData )
{
Line + = FString : : Printf ( TEXT ( " %s=%s \n " ) , * Pair . Key , * Pair . Value ) ;
}
Line + = TEXT ( " #endif /*END_REMOTE_SERVER*/ \n " ) ;
return Line ;
}
void CreateEnvironmentFromRemoteData ( const FString & String , FShaderCompilerEnvironment & OutEnvironment )
{
FString Prolog = TEXT ( " #if 0 /*BEGIN_REMOTE_SERVER*/ " ) ;
int32 FoundBegin = String . Find ( Prolog , ESearchCase : : CaseSensitive ) ;
if ( FoundBegin = = INDEX_NONE )
{
return ;
}
int32 FoundEnd = String . Find ( TEXT ( " #endif /*END_REMOTE_SERVER*/ " ) , ESearchCase : : CaseSensitive , ESearchDir : : FromStart , FoundBegin ) ;
if ( FoundEnd = = INDEX_NONE )
{
return ;
}
// +1 for EOL
const TCHAR * Ptr = & String [ FoundBegin + 1 + Prolog . Len ( ) ] ;
const TCHAR * PtrEnd = & String [ FoundEnd ] ;
while ( Ptr < PtrEnd )
{
FString Key ;
if ( ! CrossCompiler : : ParseIdentifier ( Ptr , Key ) )
{
return ;
}
if ( ! CrossCompiler : : Match ( Ptr , TEXT ( " = " ) ) )
{
return ;
}
FString Value ;
if ( ! CrossCompiler : : ParseString ( Ptr , Value ) )
{
return ;
}
if ( ! CrossCompiler : : Match ( Ptr , ' \n ' ) )
{
return ;
}
OutEnvironment . RemoteServerData . FindOrAdd ( Key ) = Value ;
}
}
2016-12-12 17:47:42 -05:00
void CompileShader_Metal ( const FShaderCompilerInput & _Input , FShaderCompilerOutput & Output , const FString & WorkingDirectory )
{
auto Input = _Input ;
2014-06-10 17:09:55 -04:00
FString PreprocessedShader ;
FShaderCompilerDefinitions AdditionalDefines ;
2015-06-24 17:40:55 -04:00
2015-09-18 12:45:20 -04:00
// Work out which standard we need, this is dependent on the shader platform.
2020-09-24 00:43:27 -04:00
// TODO: Read from toolchain class
const bool bIsMobile = FMetalCompilerToolchain : : Get ( ) - > IsMobile ( ( EShaderPlatform ) Input . Target . Platform ) ;
2017-08-24 15:38:57 -04:00
TCHAR const * StandardPlatform = nullptr ;
2015-06-24 17:40:55 -04:00
if ( bIsMobile )
{
2017-08-24 15:38:57 -04:00
StandardPlatform = TEXT ( " ios " ) ;
2015-06-24 17:40:55 -04:00
AdditionalDefines . SetDefine ( TEXT ( " IOS " ) , 1 ) ;
}
else
{
2018-09-11 14:44:10 -04:00
StandardPlatform = TEXT ( " macos " ) ;
2015-06-24 17:40:55 -04:00
AdditionalDefines . SetDefine ( TEXT ( " MAC " ) , 1 ) ;
}
2015-09-18 12:45:20 -04:00
2015-08-27 10:11:22 -04:00
AdditionalDefines . SetDefine ( TEXT ( " COMPILER_METAL " ) , 1 ) ;
2015-09-18 12:45:20 -04:00
2022-01-13 12:27:13 -05:00
EMetalGPUSemantics Semantics = EMetalGPUSemanticsMobile ;
2016-12-12 17:47:42 -05:00
2022-02-04 14:01:55 -05:00
const int32 VersionEnum = [ & Input , & Output ] ( ) - > int32
{
if ( const FString * VersionEnumEntry = Input . Environment . GetDefinitions ( ) . Find ( TEXT ( " SHADER_LANGUAGE_VERSION " ) ) )
{
return FCString : : Atoi ( * FString ( * VersionEnumEntry ) ) ;
}
else
{
new ( Output . Errors ) FShaderCompilerError ( * FString : : Printf ( TEXT ( " Missing definition of SHADER_LANGUAGE_VERSION; Falling back to default value %d " ) , GMetalDefaultShadingLanguageVersion ) ) ;
return GMetalDefaultShadingLanguageVersion ;
}
} ( ) ;
2022-01-13 12:27:13 -05:00
2020-09-24 00:43:27 -04:00
// TODO read from toolchain
2018-10-18 16:47:22 -04:00
bool bAppleTV = ( Input . ShaderFormat = = NAME_SF_METAL_TVOS | | Input . ShaderFormat = = NAME_SF_METAL_MRT_TVOS ) ;
2022-01-13 12:27:13 -05:00
if ( Input . ShaderFormat = = NAME_SF_METAL | | Input . ShaderFormat = = NAME_SF_METAL_TVOS )
2014-10-09 11:27:07 -04:00
{
2022-01-13 12:27:13 -05:00
AdditionalDefines . SetDefine ( TEXT ( " METAL_PROFILE " ) , 1 ) ;
2014-10-09 11:27:07 -04:00
}
2018-10-18 16:47:22 -04:00
else if ( Input . ShaderFormat = = NAME_SF_METAL_MRT | | Input . ShaderFormat = = NAME_SF_METAL_MRT_TVOS )
2014-10-09 11:27:07 -04:00
{
2017-03-21 17:46:52 -04:00
AdditionalDefines . SetDefine ( TEXT ( " METAL_MRT_PROFILE " ) , 1 ) ;
2017-04-11 10:32:07 -04:00
Semantics = EMetalGPUSemanticsTBDRDesktop ;
2014-10-09 11:27:07 -04:00
}
2016-02-26 05:49:37 -05:00
else if ( Input . ShaderFormat = = NAME_SF_METAL_MACES3_1 )
{
AdditionalDefines . SetDefine ( TEXT ( " METAL_PROFILE " ) , 1 ) ;
2017-04-11 10:32:07 -04:00
Semantics = EMetalGPUSemanticsImmediateDesktop ;
2016-02-26 05:49:37 -05:00
}
2015-06-24 17:40:55 -04:00
else if ( Input . ShaderFormat = = NAME_SF_METAL_SM5 )
{
AdditionalDefines . SetDefine ( TEXT ( " METAL_SM5_PROFILE " ) , 1 ) ;
2015-09-18 12:45:20 -04:00
AdditionalDefines . SetDefine ( TEXT ( " USING_VERTEX_SHADER_LAYER " ) , 1 ) ;
2017-04-11 10:32:07 -04:00
Semantics = EMetalGPUSemanticsImmediateDesktop ;
}
else if ( Input . ShaderFormat = = NAME_SF_METAL_MRT_MAC )
{
AdditionalDefines . SetDefine ( TEXT ( " METAL_MRT_PROFILE " ) , 1 ) ;
Semantics = EMetalGPUSemanticsTBDRDesktop ;
2015-06-24 17:40:55 -04:00
}
2014-10-09 11:27:07 -04:00
else
{
Output . bSucceeded = false ;
new ( Output . Errors ) FShaderCompilerError ( * FString : : Printf ( TEXT ( " Invalid shader format '%s' passed to compiler. " ) , * Input . ShaderFormat . ToString ( ) ) ) ;
return ;
}
2017-03-21 17:46:52 -04:00
2019-06-11 18:27:07 -04:00
2020-09-24 00:43:27 -04:00
AdditionalDefines . SetDefine ( TEXT ( " COMPILER_HLSLCC " ) , 2 ) ;
2019-06-11 18:27:07 -04:00
2022-01-13 12:27:13 -05:00
EMetalTypeBufferMode TypeMode = EMetalTypeBufferModeRaw ;
2017-08-24 15:38:57 -04:00
FString MinOSVersion ;
FString StandardVersion ;
2022-09-20 03:10:30 -04:00
TypeMode = EMetalTypeBufferModeTB ;
2017-08-24 15:38:57 -04:00
switch ( VersionEnum )
2017-09-25 14:08:25 -04:00
{
2022-06-27 04:12:37 -04:00
case 8 :
StandardVersion = TEXT ( " 3.0 " ) ;
if ( bAppleTV )
{
MinOSVersion = TEXT ( " -mtvos-version-min=16.0 " ) ;
}
else if ( bIsMobile )
{
MinOSVersion = TEXT ( " -mios-version-min=16.0 " ) ;
}
else
{
MinOSVersion = TEXT ( " -mmacosx-version-min=13 " ) ;
}
break ;
2022-01-13 12:27:13 -05:00
case 7 :
StandardVersion = TEXT ( " 2.4 " ) ;
if ( bAppleTV )
{
MinOSVersion = TEXT ( " -mtvos-version-min=15.0 " ) ;
}
else if ( bIsMobile )
{
MinOSVersion = TEXT ( " -mios-version-min=15.0 " ) ;
}
else
{
MinOSVersion = TEXT ( " -mmacosx-version-min=12 " ) ;
}
break ;
2022-09-20 03:10:30 -04:00
case 0 :
StandardVersion = TEXT ( " 2.4 " ) ;
2022-01-13 12:27:13 -05:00
if ( bAppleTV )
{
2022-09-20 03:10:30 -04:00
MinOSVersion = TEXT ( " -mtvos-version-min=15.0 " ) ;
2022-01-13 12:27:13 -05:00
}
else if ( bIsMobile )
{
2022-09-20 03:10:30 -04:00
MinOSVersion = TEXT ( " -mios-version-min=15.0 " ) ;
2022-01-13 12:27:13 -05:00
}
else
{
2022-09-20 03:10:30 -04:00
MinOSVersion = TEXT ( " -mmacosx-version-min=12 " ) ;
2022-01-13 12:27:13 -05:00
}
2022-09-20 03:10:30 -04:00
//EIOSMetalShaderStandard::IOSMetalSLStandard_Minimum
//EMacMetalShaderStandard::MacMetalSLStandard_Minimum
2022-01-13 12:27:13 -05:00
break ;
2022-09-20 03:10:30 -04:00
default :
2022-01-13 12:27:13 -05:00
Output . bSucceeded = false ;
{
2022-09-20 03:10:30 -04:00
FString EngineIdentifier = FEngineVersion : : Current ( ) . ToString ( EVersionComponent : : Minor ) ;
2022-01-13 12:27:13 -05:00
FShaderCompilerError * NewError = new ( Output . Errors ) FShaderCompilerError ( ) ;
2022-09-20 03:10:30 -04:00
NewError - > StrippedErrorMessage = FString : : Printf ( TEXT ( " Minimum Metal Version is 2.4 in UE %s " ) , * EngineIdentifier ) ;
2022-01-13 12:27:13 -05:00
return ;
}
break ;
2017-09-25 14:08:25 -04:00
}
2022-04-05 11:51:04 -04:00
if ( Input . Environment . FullPrecisionInPS )
{
AdditionalDefines . SetDefine ( TEXT ( " FORCE_FLOATS " ) , ( uint32 ) 1 ) ;
}
2022-01-13 12:27:13 -05:00
2022-06-27 04:12:37 -04:00
FString Standard ;
if ( VersionEnum > = 8 )
{
Standard = FString : : Printf ( TEXT ( " -std=metal%s " ) , * StandardVersion ) ;
}
else
{
Standard = FString : : Printf ( TEXT ( " -std=%s-metal%s " ) , StandardPlatform , * StandardVersion ) ;
}
2014-06-10 17:09:55 -04:00
2017-10-04 13:36:51 -04:00
bool const bDirectCompile = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " directcompile " ) ) ;
if ( bDirectCompile )
{
Input . DumpDebugInfoPath = FPaths : : GetPath ( Input . VirtualSourceFilePath ) ;
}
2014-06-10 17:09:55 -04:00
const bool bDumpDebugInfo = ( Input . DumpDebugInfoPath ! = TEXT ( " " ) & & IFileManager : : Get ( ) . DirectoryExists ( * Input . DumpDebugInfoPath ) ) ;
2017-08-24 15:38:57 -04:00
// Allow the shader pipeline to override the platform default in here.
uint32 MaxUnrollLoops = 32 ;
if ( Input . Environment . CompilerFlags . Contains ( CFLAG_AvoidFlowControl ) )
2015-06-24 17:40:55 -04:00
{
2017-08-24 15:38:57 -04:00
AdditionalDefines . SetDefine ( TEXT ( " COMPILER_SUPPORTS_ATTRIBUTES " ) , ( uint32 ) 0 ) ;
MaxUnrollLoops = 1024 ; // Max. permitted by hlslcc
}
else if ( Input . Environment . CompilerFlags . Contains ( CFLAG_PreferFlowControl ) )
{
AdditionalDefines . SetDefine ( TEXT ( " COMPILER_SUPPORTS_ATTRIBUTES " ) , ( uint32 ) 0 ) ;
MaxUnrollLoops = 0 ;
2015-06-24 17:40:55 -04:00
}
else
{
2017-08-24 15:38:57 -04:00
AdditionalDefines . SetDefine ( TEXT ( " COMPILER_SUPPORTS_ATTRIBUTES " ) , ( uint32 ) 1 ) ;
2015-06-24 17:40:55 -04:00
}
2021-06-07 14:30:42 -04:00
AdditionalDefines . SetDefine ( TEXT ( " COMPILER_SUPPORTS_DUAL_SOURCE_BLENDING_SLOT_DECORATION " ) , ( uint32 ) 1 ) ;
2017-08-24 15:38:57 -04:00
if ( Input . bSkipPreprocessedCache )
2014-06-10 17:09:55 -04:00
{
2017-08-24 15:38:57 -04:00
if ( ! FFileHelper : : LoadFileToString ( PreprocessedShader , * Input . VirtualSourceFilePath ) )
{
return ;
}
// Remove const as we are on debug-only mode
CrossCompiler : : CreateEnvironmentFromResourceTable ( PreprocessedShader , ( FShaderCompilerEnvironment & ) Input . Environment ) ;
2017-10-24 10:14:07 -04:00
CreateEnvironmentFromRemoteData ( PreprocessedShader , ( FShaderCompilerEnvironment & ) Input . Environment ) ;
2017-08-24 15:38:57 -04:00
}
else
{
if ( ! PreprocessShader ( PreprocessedShader , Output , Input , AdditionalDefines ) )
{
// The preprocessing stage will add any relevant errors.
return ;
}
}
char * MetalShaderSource = NULL ;
char * ErrorLog = NULL ;
2014-06-10 17:09:55 -04:00
2021-09-02 16:59:27 -04:00
const EShaderFrequency Frequency = ( EShaderFrequency ) Input . Target . Frequency ;
2021-09-02 16:41:03 -04:00
if ( ! ( Frequency = = SF_Vertex | | Frequency = = SF_Pixel | | Frequency = = SF_Compute ) )
2017-08-24 15:38:57 -04:00
{
Output . bSucceeded = false ;
FShaderCompilerError * NewError = new ( Output . Errors ) FShaderCompilerError ( ) ;
NewError - > StrippedErrorMessage = FString : : Printf (
TEXT ( " %s shaders not supported for use in Metal. " ) ,
2021-09-02 16:41:03 -04:00
CrossCompiler : : GetFrequencyName ( Frequency )
2017-08-24 15:38:57 -04:00
) ;
return ;
}
2020-01-24 18:07:01 -05:00
FShaderParameterParser ShaderParameterParser ;
2022-07-15 12:25:44 -04:00
if ( ! ShaderParameterParser . ParseAndModify ( Input , Output , PreprocessedShader , /* ConstantBufferType = */ nullptr ) )
2020-01-24 18:07:01 -05:00
{
// The FShaderParameterParser will add any relevant errors.
return ;
}
2017-08-24 15:38:57 -04:00
// This requires removing the HLSLCC_NoPreprocess flag later on!
2020-01-24 18:07:01 -05:00
RemoveUniformBuffersFromSource ( Input . Environment , PreprocessedShader ) ;
2022-04-06 17:19:38 -04:00
// Process TEXT macro.
TransformStringIntoCharacterArray ( PreprocessedShader ) ;
2019-09-14 09:45:25 -04:00
2019-06-11 18:27:07 -04:00
uint32 CCFlags = HLSLCC_NoPreprocess | HLSLCC_PackUniformsIntoUniformBufferWithNames | HLSLCC_FixAtomicReferences | HLSLCC_RetainSizes | HLSLCC_KeepSamplerAndImageNames ;
2019-01-07 17:35:21 -05:00
if ( ! bDirectCompile | | UE_BUILD_DEBUG )
{
// Validation is expensive - only do it when compiling directly for debugging
CCFlags | = HLSLCC_NoValidation ;
}
// Required as we added the RemoveUniformBuffersFromSource() function (the cross-compiler won't be able to interpret comments w/o a preprocessor)
CCFlags & = ~ HLSLCC_NoPreprocess ;
2017-08-24 15:38:57 -04:00
// Write out the preprocessed file and a batch file to compile it if requested (DumpDebugInfoPath is valid)
2018-03-24 09:22:20 -04:00
if ( bDumpDebugInfo & & ! bDirectCompile )
2017-08-24 15:38:57 -04:00
{
FArchive * FileWriter = IFileManager : : Get ( ) . CreateFileWriter ( * ( Input . DumpDebugInfoPath / FPaths : : GetBaseFilename ( Input . GetSourceFilename ( ) + TEXT ( " .usf " ) ) ) ) ;
if ( FileWriter )
2014-06-10 17:09:55 -04:00
{
2020-09-24 00:43:27 -04:00
FString Line = GetDumpDebugUSFContents ( Input , PreprocessedShader , 0 ) ;
2017-10-24 10:14:07 -04:00
2020-09-24 00:43:27 -04:00
// add the remote data if necessary
// if (IsRemoteBuildingConfigured(&Input.Environment))
{
Line + = CreateRemoteDataFromEnvironment ( Input . Environment ) ;
2016-06-08 16:02:23 -04:00
}
2020-09-24 00:43:27 -04:00
FileWriter - > Serialize ( TCHAR_TO_ANSI ( * Line ) , Line . Len ( ) ) ;
2017-08-24 15:38:57 -04:00
FileWriter - > Close ( ) ;
delete FileWriter ;
2014-06-10 17:09:55 -04:00
}
2017-08-24 15:38:57 -04:00
if ( Input . bGenerateDirectCompileFile )
2014-06-10 17:09:55 -04:00
{
2019-01-07 17:35:21 -05:00
FFileHelper : : SaveStringToFile ( CreateShaderCompilerWorkerDirectCommandLine ( Input , CCFlags ) , * ( Input . DumpDebugInfoPath / TEXT ( " DirectCompile.txt " ) ) ) ;
2014-06-10 17:09:55 -04:00
}
2017-08-24 15:38:57 -04:00
}
2014-06-10 17:09:55 -04:00
2017-08-24 15:38:57 -04:00
FSHAHash GUIDHash ;
if ( ! bDirectCompile )
{
TArray < FString > GUIDFiles ;
2018-09-11 14:44:10 -04:00
GUIDFiles . Add ( FPaths : : ConvertRelativePathToFull ( TEXT ( " /Engine/Public/Platform/Metal/MetalCommon.ush " ) ) ) ;
2017-08-24 15:38:57 -04:00
GUIDFiles . Add ( FPaths : : ConvertRelativePathToFull ( TEXT ( " /Engine/Public/ShaderVersion.ush " ) ) ) ;
2018-09-11 14:44:10 -04:00
GUIDHash = GetShaderFilesHash ( GUIDFiles , Input . Target . GetPlatform ( ) ) ;
2017-08-24 15:38:57 -04:00
}
else
{
FGuid Guid = FGuid : : NewGuid ( ) ;
FSHA1 : : HashBuffer ( & Guid , sizeof ( FGuid ) , GUIDHash . Hash ) ;
}
2015-03-06 13:09:36 -05:00
2021-11-18 14:37:34 -05:00
bool bCompiled = DoCompileMetalShader ( Input , Output , WorkingDirectory , PreprocessedShader , GUIDHash , VersionEnum , CCFlags , Semantics , TypeMode , MaxUnrollLoops , Frequency , bDumpDebugInfo , Standard , MinOSVersion ) ;
if ( bCompiled & & Output . bSucceeded )
2017-08-24 15:38:57 -04:00
{
2021-11-18 14:37:34 -05:00
2014-06-10 17:09:55 -04:00
}
2020-01-24 18:07:01 -05:00
2021-11-18 15:53:43 -05:00
ShaderParameterParser . ValidateShaderParameterTypes ( Input , bIsMobile , Output ) ;
2014-06-10 17:09:55 -04:00
}
2017-04-11 10:32:07 -04:00
2017-07-13 10:13:07 -04:00
bool StripShader_Metal ( TArray < uint8 > & Code , class FString const & DebugPath , bool const bNative )
2017-04-11 10:32:07 -04:00
{
2017-07-13 10:13:07 -04:00
bool bSuccess = false ;
2017-04-11 10:32:07 -04:00
FShaderCodeReader ShaderCode ( Code ) ;
FMemoryReader Ar ( Code , true ) ;
Ar . SetLimitSize ( ShaderCode . GetActualShaderCodeSize ( ) ) ;
// was the shader already compiled offline?
uint8 OfflineCompiledFlag ;
Ar < < OfflineCompiledFlag ;
2017-07-21 21:01:33 -04:00
if ( bNative & & OfflineCompiledFlag = = 1 )
2017-04-11 10:32:07 -04:00
{
2017-07-13 10:13:07 -04:00
// get the header
2018-09-11 14:44:10 -04:00
FMetalCodeHeader Header ;
2017-07-13 10:13:07 -04:00
Ar < < Header ;
2021-11-18 14:37:34 -05:00
const FString ShaderName = ShaderCode . FindOptionalData ( FShaderCodeName : : Key ) ;
2017-07-13 10:13:07 -04:00
// Must be compiled for archiving or something is very wrong.
2017-07-21 21:01:33 -04:00
if ( bNative = = false | | Header . CompileFlags & ( 1 < < CFLAG_Archive ) )
2017-04-11 10:32:07 -04:00
{
2017-07-13 10:13:07 -04:00
bSuccess = true ;
// remember where the header ended and code (precompiled or source) begins
int32 CodeOffset = Ar . Tell ( ) ;
const uint8 * SourceCodePtr = ( uint8 * ) Code . GetData ( ) + CodeOffset ;
// Copy the non-optional shader bytecode
TArray < uint8 > SourceCode ;
SourceCode . Append ( SourceCodePtr , ShaderCode . GetActualShaderCodeSize ( ) - CodeOffset ) ;
const ANSICHAR * ShaderSource = ShaderCode . FindOptionalData ( ' c ' ) ;
2018-09-27 15:23:31 -04:00
const size_t ShaderSourceLength = ShaderSource ? FCStringAnsi : : Strlen ( ShaderSource ) : 0 ;
bool const bHasShaderSource = ShaderSourceLength > 0 ;
2017-07-13 10:13:07 -04:00
const ANSICHAR * ShaderPath = ShaderCode . FindOptionalData ( ' p ' ) ;
bool const bHasShaderPath = ( ShaderPath & & FCStringAnsi : : Strlen ( ShaderPath ) > 0 ) ;
if ( bHasShaderSource & & bHasShaderPath )
{
FString DebugFilePath = DebugPath / FString ( ShaderPath ) ;
FString DebugFolderPath = FPaths : : GetPath ( DebugFilePath ) ;
if ( IFileManager : : Get ( ) . MakeDirectory ( * DebugFolderPath , true ) )
{
FString TempPath = FPaths : : CreateTempFilename ( * DebugFolderPath , TEXT ( " MetalShaderFile- " ) , TEXT ( " .metal " ) ) ;
2018-09-27 15:23:31 -04:00
IPlatformFile & PlatformFile = FPlatformFileManager : : Get ( ) . GetPlatformFile ( ) ;
IFileHandle * FileHandle = PlatformFile . OpenWrite ( * TempPath ) ;
if ( FileHandle )
{
FileHandle - > Write ( ( const uint8 * ) ShaderSource , ShaderSourceLength ) ;
delete FileHandle ;
2019-08-02 07:07:41 -04:00
IFileManager : : Get ( ) . Move ( * DebugFilePath , * TempPath , true , false , true , false ) ;
2018-09-27 15:23:31 -04:00
IFileManager : : Get ( ) . Delete ( * TempPath ) ;
}
else
{
2021-11-18 14:37:34 -05:00
UE_LOG ( LogShaders , Error , TEXT ( " Shader stripping failed: shader %s (Len: %0.8x, CRC: %0.8x) failed to create file %s! " ) , * ShaderName , Header . SourceLen , Header . SourceCRC , * TempPath ) ;
2018-09-27 15:23:31 -04:00
}
2017-07-13 10:13:07 -04:00
}
}
if ( bNative )
{
int32 ObjectSize = 0 ;
const uint8 * ShaderObject = ShaderCode . FindOptionalDataAndSize ( ' o ' , ObjectSize ) ;
2018-02-22 11:25:06 -05:00
// If ShaderObject and ObjectSize is zero then the code has already been stripped - source code should be the byte code
if ( ShaderObject & & ObjectSize )
{
TArray < uint8 > ObjectCodeArray ;
ObjectCodeArray . Append ( ShaderObject , ObjectSize ) ;
SourceCode = ObjectCodeArray ;
}
2017-07-13 10:13:07 -04:00
}
// Strip any optional data
if ( bNative | | ShaderCode . GetOptionalDataSize ( ) > 0 )
{
// Write out the header and compiled shader code
FShaderCode NewCode ;
FMemoryWriter NewAr ( NewCode . GetWriteAccess ( ) , true ) ;
NewAr < < OfflineCompiledFlag ;
NewAr < < Header ;
// jam it into the output bytes
NewAr . Serialize ( SourceCode . GetData ( ) , SourceCode . Num ( ) ) ;
Code = NewCode . GetReadAccess ( ) ;
}
2017-04-11 10:32:07 -04:00
}
2017-07-21 21:01:33 -04:00
else
{
2021-11-18 14:37:34 -05:00
UE_LOG ( LogShaders , Error , TEXT ( " Shader stripping failed: shader %s (Len: %0.8x, CRC: %0.8x) was not compiled for archiving into a native library (Native: %s, Compile Flags: %0.8x)! " ) , * ShaderName , Header . SourceLen , Header . SourceCRC , bNative ? TEXT ( " true " ) : TEXT ( " false " ) , ( uint32 ) Header . CompileFlags ) ;
2017-07-21 21:01:33 -04:00
}
}
else
{
UE_LOG ( LogShaders , Error , TEXT ( " Shader stripping failed: shader %s (Native: %s, Offline Compiled: %d) was not compiled to bytecode for native archiving! " ) , * DebugPath , bNative ? TEXT ( " true " ) : TEXT ( " false " ) , OfflineCompiledFlag ) ;
2017-04-11 10:32:07 -04:00
}
2017-07-13 10:13:07 -04:00
return bSuccess ;
2017-04-11 10:32:07 -04:00
}
2022-07-04 19:06:33 -04:00
uint64 AppendShader_Metal ( FString const & WorkingDir , const FSHAHash & Hash , TArray < uint8 > & InShaderCode )
2017-04-11 10:32:07 -04:00
{
2017-07-13 10:13:07 -04:00
uint64 Id = 0 ;
2020-09-24 00:43:27 -04:00
const bool bCompilerAvailable = FMetalCompilerToolchain : : Get ( ) - > IsCompilerAvailable ( ) ;
2017-07-13 10:13:07 -04:00
2020-09-24 00:43:27 -04:00
if ( bCompilerAvailable )
2017-04-11 10:32:07 -04:00
{
2017-07-13 10:13:07 -04:00
// Parse the existing data and extract the source code. We have to recompile it
FShaderCodeReader ShaderCode ( InShaderCode ) ;
FMemoryReader Ar ( InShaderCode , true ) ;
Ar . SetLimitSize ( ShaderCode . GetActualShaderCodeSize ( ) ) ;
// was the shader already compiled offline?
uint8 OfflineCompiledFlag ;
Ar < < OfflineCompiledFlag ;
2017-07-21 21:01:33 -04:00
if ( OfflineCompiledFlag = = 1 )
2017-04-11 10:32:07 -04:00
{
2017-07-21 21:01:33 -04:00
// get the header
2018-09-11 14:44:10 -04:00
FMetalCodeHeader Header ;
2017-07-21 21:01:33 -04:00
Ar < < Header ;
2021-11-18 14:37:34 -05:00
const FString ShaderName = ShaderCode . FindOptionalData ( FShaderCodeName : : Key ) ;
2017-07-21 21:01:33 -04:00
// Must be compiled for archiving or something is very wrong.
if ( Header . CompileFlags & ( 1 < < CFLAG_Archive ) )
2017-04-11 10:32:07 -04:00
{
2017-07-21 21:01:33 -04:00
// remember where the header ended and code (precompiled or source) begins
int32 CodeOffset = Ar . Tell ( ) ;
const uint8 * SourceCodePtr = ( uint8 * ) InShaderCode . GetData ( ) + CodeOffset ;
// Copy the non-optional shader bytecode
2017-09-15 11:26:46 -04:00
int32 ObjectCodeDataSize = 0 ;
uint8 const * Object = ShaderCode . FindOptionalDataAndSize ( ' o ' , ObjectCodeDataSize ) ;
2021-11-18 14:37:34 -05:00
2018-02-22 11:25:06 -05:00
// 'o' segment missing this is a pre stripped shader
if ( ! Object )
{
ObjectCodeDataSize = ShaderCode . GetActualShaderCodeSize ( ) - CodeOffset ;
Object = SourceCodePtr ;
}
2017-09-15 11:26:46 -04:00
TArrayView < const uint8 > ObjectCodeArray ( Object , ObjectCodeDataSize ) ;
2017-07-21 21:01:33 -04:00
// Object code segment
FString ObjFilename = WorkingDir / FString : : Printf ( TEXT ( " Main_%0.8x_%0.8x.o " ) , Header . SourceLen , Header . SourceCRC ) ;
bool const bHasObjectData = ( ObjectCodeDataSize > 0 ) | | IFileManager : : Get ( ) . FileExists ( * ObjFilename ) ;
if ( bHasObjectData )
2017-07-13 10:13:07 -04:00
{
2017-07-21 21:01:33 -04:00
// metal commandlines
int32 ReturnCode = 0 ;
FString Results ;
FString Errors ;
bool bHasObjectFile = IFileManager : : Get ( ) . FileExists ( * ObjFilename ) ;
if ( ObjectCodeDataSize > 0 )
{
// write out shader object code source (IR) for archiving to a single library file later
if ( FFileHelper : : SaveArrayToFile ( ObjectCodeArray , * ObjFilename ) )
{
bHasObjectFile = true ;
}
}
if ( bHasObjectFile )
{
Id = ( ( uint64 ) Header . SourceLen < < 32 ) | Header . SourceCRC ;
// This is going to get serialised into the shader resource archive we don't anything but the header info now with the archive flag set
Header . CompileFlags | = ( 1 < < CFLAG_Archive ) ;
// Write out the header and compiled shader code
FShaderCode NewCode ;
FMemoryWriter NewAr ( NewCode . GetWriteAccess ( ) , true ) ;
NewAr < < OfflineCompiledFlag ;
NewAr < < Header ;
InShaderCode = NewCode . GetReadAccess ( ) ;
2021-11-18 14:37:34 -05:00
UE_LOG ( LogShaders , Verbose , TEXT ( " Archiving succeeded: shader %s (Len: %0.8x, CRC: %0.8x, SHA: %s) " ) , * ShaderName , Header . SourceLen , Header . SourceCRC , * Hash . ToString ( ) ) ;
2017-07-21 21:01:33 -04:00
}
else
{
2021-11-18 14:37:34 -05:00
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: failed to write temporary file %s for shader %s (Len: %0.8x, CRC: %0.8x, SHA: %s) " ) , * ObjFilename , * ShaderName , Header . SourceLen , Header . SourceCRC , * Hash . ToString ( ) ) ;
2017-07-21 21:01:33 -04:00
}
}
else
{
2021-11-18 14:37:34 -05:00
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: shader %s (Len: %0.8x, CRC: %0.8x, SHA: %s) has no object data " ) , * ShaderName , Header . SourceLen , Header . SourceCRC , * Hash . ToString ( ) ) ;
2017-07-13 10:13:07 -04:00
}
2017-04-11 10:32:07 -04:00
}
else
{
2021-11-18 14:37:34 -05:00
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: shader %s (Len: %0.8x, CRC: %0.8x, SHA: %s) was not compiled for archiving (Compile Flags: %0.8x)! " ) , * ShaderName , Header . SourceLen , Header . SourceCRC , * Hash . ToString ( ) , ( uint32 ) Header . CompileFlags ) ;
2017-04-11 10:32:07 -04:00
}
}
else
{
2017-07-21 21:01:33 -04:00
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: shader SHA: %s was not compiled to bytecode (%d)! " ) , * Hash . ToString ( ) , OfflineCompiledFlag ) ;
2017-04-11 10:32:07 -04:00
}
}
else
{
2017-08-24 15:38:57 -04:00
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: no Xcode install on the local machine or a remote Mac. " ) ) ;
2017-04-11 10:32:07 -04:00
}
return Id ;
}
bool FinalizeLibrary_Metal ( FName const & Format , FString const & WorkingDir , FString const & LibraryPath , TSet < uint64 > const & Shaders , class FString const & DebugOutputDir )
{
bool bOK = false ;
2017-07-13 10:13:07 -04:00
2020-09-24 00:43:27 -04:00
FString FullyQualifiedWorkingDir = FPaths : : ConvertRelativePathToFull ( WorkingDir ) ;
const FMetalCompilerToolchain * Toolchain = FMetalCompilerToolchain : : Get ( ) ;
const bool bCompilerAvailable = Toolchain - > IsCompilerAvailable ( ) ;
EShaderPlatform Platform = FMetalCompilerToolchain : : MetalShaderFormatToLegacyShaderPlatform ( Format ) ;
const EAppleSDKType SDK = FMetalCompilerToolchain : : MetalShaderPlatformToSDK ( Platform ) ;
if ( bCompilerAvailable )
2017-04-11 10:32:07 -04:00
{
2017-07-13 10:13:07 -04:00
int32 ReturnCode = 0 ;
FString Results ;
FString Errors ;
2020-09-24 00:43:27 -04:00
// WARNING: This may be called from multiple threads using the same WorkingDir. All the temporary files must be uniquely named.
// The local path that will end up with the archive.
FString LocalArchivePath = FPaths : : CreateTempFilename ( * FullyQualifiedWorkingDir , TEXT ( " MetalArchive " ) , TEXT ( " " ) ) + TEXT ( " .metalar " ) ;
IFileManager : : Get ( ) . Delete ( * LocalArchivePath ) ;
2017-07-13 10:13:07 -04:00
IFileManager : : Get ( ) . Delete ( * LibraryPath ) ;
2020-09-24 00:43:27 -04:00
UE_LOG ( LogMetalShaderCompiler , Display , TEXT ( " Creating Native Library %s " ) , * LibraryPath ) ;
2017-07-13 10:13:07 -04:00
2020-09-24 00:43:27 -04:00
bool bArchiveFileValid = false ;
2017-07-13 10:13:07 -04:00
// Archive build phase - like unix ar, build metal archive from all the object files
{
2020-09-24 00:43:27 -04:00
UE_LOG ( LogMetalShaderCompiler , Display , TEXT ( " Archiving %d shaders for shader platform: %s " ) , Shaders . Num ( ) , * Format . GetPlainNameString ( ) ) ;
2017-07-13 10:13:07 -04:00
2020-09-24 00:43:27 -04:00
/*
AR type utils can use 'M' scripts
They look like this:
CREATE MyArchive.metalar
ADDMOD Shader1.metal
ADDMOD Shader2.metal
SAVE
END
*/
FString M_Script = FString : : Printf ( TEXT ( " CREATE \" %s \" \n " ) , * FPaths : : ConvertRelativePathToFull ( LocalArchivePath ) ) ;
uint32 Index = 0 ;
2017-04-11 10:32:07 -04:00
for ( auto Shader : Shaders )
{
uint32 Len = ( Shader > > 32 ) ;
uint32 CRC = ( Shader & 0xffffffff ) ;
2020-09-24 00:43:27 -04:00
FString FileName = FString : : Printf ( TEXT ( " Main_%0.8x_%0.8x.o " ) , Len , CRC ) ;
UE_LOG ( LogMetalShaderCompiler , Verbose , TEXT ( " [%d/%d] %s %s " ) , + + Index , Shaders . Num ( ) , * Format . GetPlainNameString ( ) , * FileName ) ;
FString SourceFilePath = FString : : Printf ( TEXT ( " \" %s/%s \" " ) , * FullyQualifiedWorkingDir , * FileName ) ;
M_Script + = FString : : Printf ( TEXT ( " ADDMOD %s \n " ) , * SourceFilePath ) ;
2017-04-11 10:32:07 -04:00
}
2020-09-24 00:43:27 -04:00
M_Script + = FString ( TEXT ( " SAVE \n " ) ) ;
M_Script + = FString ( TEXT ( " END \n " ) ) ;
FString LocalScriptFilePath = FPaths : : CreateTempFilename ( * FullyQualifiedWorkingDir , TEXT ( " MetalArScript " ) , TEXT ( " .M " ) ) ;
FFileHelper : : SaveStringToFile ( M_Script , * LocalScriptFilePath ) ;
2021-07-22 11:11:51 -04:00
if ( ! FPaths : : FileExists ( LocalScriptFilePath ) )
2017-04-11 10:32:07 -04:00
{
2020-09-24 00:43:27 -04:00
UE_LOG ( LogMetalShaderCompiler , Error , TEXT ( " Failed to create metal-ar .M script at %s " ) , * LocalScriptFilePath ) ;
return false ;
}
FPaths : : MakePlatformFilename ( LocalScriptFilePath ) ;
bool bSuccess = Toolchain - > ExecMetalAr ( SDK , * LocalScriptFilePath , & ReturnCode , & Results , & Errors ) ;
2021-07-22 11:11:51 -04:00
bArchiveFileValid = FPaths : : FileExists ( LocalArchivePath ) ;
2020-09-24 00:43:27 -04:00
if ( ReturnCode ! = 0 | | ! bArchiveFileValid )
{
UE_LOG ( LogMetalShaderCompiler , Error , TEXT ( " Archiving failed: metal-ar failed with code %d: %s %s " ) , ReturnCode , * Results , * Errors ) ;
return false ;
2017-04-11 10:32:07 -04:00
}
2017-07-13 10:13:07 -04:00
}
// Lib build phase, metalar to metallib
{
2017-04-11 10:32:07 -04:00
// handle compile error
2017-07-13 10:13:07 -04:00
if ( ReturnCode = = 0 & & bArchiveFileValid )
2017-04-11 10:32:07 -04:00
{
2020-09-24 00:43:27 -04:00
UE_LOG ( LogMetalShaderCompiler , Display , TEXT ( " Post-processing archive for shader platform: %s " ) , * Format . GetPlainNameString ( ) ) ;
2018-09-27 15:23:31 -04:00
2020-09-24 00:43:27 -04:00
FString LocalMetalLibPath = LibraryPath ;
2018-09-27 15:23:31 -04:00
2020-09-24 00:43:27 -04:00
if ( FPaths : : FileExists ( LocalMetalLibPath ) )
{
UE_LOG ( LogMetalShaderCompiler , Warning , TEXT ( " Archiving warning: target metallib already exists and will be overwritten: %s " ) , * LocalMetalLibPath ) ;
2018-09-27 15:23:31 -04:00
}
2019-12-02 19:28:01 -05:00
2020-09-24 00:43:27 -04:00
FString MetallibParams = FString : : Printf ( TEXT ( " -o \" %s \" \" %s \" " ) , * LocalMetalLibPath , * LocalArchivePath ) ;
2018-09-27 15:23:31 -04:00
ReturnCode = 0 ;
2020-09-24 00:43:27 -04:00
Results . Empty ( ) ;
Errors . Empty ( ) ;
2020-01-24 18:07:01 -05:00
2020-09-24 00:43:27 -04:00
bool bSuccess = Toolchain - > ExecMetalLib ( SDK , * MetallibParams , & ReturnCode , & Results , & Errors ) ;
2018-09-27 15:23:31 -04:00
// handle compile error
2020-09-24 00:43:27 -04:00
if ( bSuccess & & ReturnCode = = 0 )
2018-09-27 15:23:31 -04:00
{
2020-09-24 00:43:27 -04:00
check ( LocalMetalLibPath = = LibraryPath ) ;
2018-09-27 15:23:31 -04:00
bOK = ( IFileManager : : Get ( ) . FileSize ( * LibraryPath ) > 0 ) ;
if ( ! bOK )
{
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: failed to copy to local destination: %s " ) , * LibraryPath ) ;
}
2017-04-11 10:32:07 -04:00
}
else
{
2020-09-24 00:43:27 -04:00
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: metallib failed with code %d: %s %s " ) , ReturnCode , * Results , * Errors ) ;
2017-04-11 10:32:07 -04:00
}
}
2017-07-13 10:13:07 -04:00
else
{
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: no valid input for metallib. " ) ) ;
}
2017-04-11 10:32:07 -04:00
}
}
else
{
UE_LOG ( LogShaders , Error , TEXT ( " Archiving failed: no Xcode install. " ) ) ;
}
return bOK ;
}