Files
UnrealEngineUWP/Engine/Source/Developer/iOS/MetalShaderFormat/Private/MetalShaderCompiler.cpp
Mark Satterthwaite 977542aeaa Merging general rendering & shader changes necessary to support desktop Metal.
reviewedby michael.trepka, rolando.caloca, lee.clark

[CL 2670859 by Mark Satterthwaite in Main branch]
2015-08-27 10:11:22 -04:00

654 lines
22 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
// ..
#include "MetalShaderFormat.h"
#include "Core.h"
#include "ShaderCore.h"
#include "MetalShaderResources.h"
#include "ShaderCompilerCommon.h"
#if PLATFORM_WINDOWS
#include "AllowWindowsPlatformTypes.h"
#include "Windows/PreWindowsApi.h"
#include <objbase.h>
#include <assert.h>
#include <stdio.h>
#include "Windows/PostWindowsApi.h"
#include "Windows/MinWindows.h"
#include "HideWindowsPlatformTypes.h"
#endif
#include "ShaderPreprocessor.h"
#include "hlslcc.h"
#include "MetalBackend.h"
DEFINE_LOG_CATEGORY_STATIC(LogMetalShaderCompiler, Log, All);
/*------------------------------------------------------------------------------
Shader compiling.
------------------------------------------------------------------------------*/
static inline uint32 ParseNumber(const TCHAR* Str)
{
uint32 Num = 0;
while (*Str && *Str >= '0' && *Str <= '9')
{
Num = Num * 10 + *Str++ - '0';
}
return Num;
}
/**
* 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.
*/
static void BuildMetalShaderOutput(
FShaderCompilerOutput& ShaderOutput,
const FShaderCompilerInput& ShaderInput,
const ANSICHAR* InShaderSource,
int32 SourceLen,
bool bIsMobile,
TArray<FShaderCompilerError>& OutErrors
)
{
const ANSICHAR* USFSource = InShaderSource;
CrossCompiler::FHlslccHeader CCHeader;
if (!CCHeader.Read(USFSource, SourceLen))
{
UE_LOG(LogMetalShaderCompiler, Fatal, TEXT("Bad hlslcc header found"));
}
FMetalCodeHeader Header = {0};
FShaderParameterMap& ParameterMap = ShaderOutput.ParameterMap;
EShaderFrequency Frequency = (EShaderFrequency)ShaderOutput.Target.Frequency;
TBitArray<> UsedUniformBufferSlots;
UsedUniformBufferSlots.Init(false,32);
// Write out the magic markers.
Header.Frequency = Frequency;
// Only inputs for vertex shaders must be tracked.
if (Frequency == SF_Vertex)
{
static const FString AttributePrefix = TEXT("in_ATTRIBUTE");
for (auto& Input : CCHeader.Inputs)
{
// Only process attributes.
if (Input.Name.StartsWith(AttributePrefix))
{
uint8 AttributeIndex = ParseNumber(*Input.Name + AttributePrefix.Len());
Header.Bindings.InOutMask |= (1 << AttributeIndex);
}
}
}
// Then the list of outputs.
static const FString TargetPrefix = "out_Target";
static const FString GL_FragDepth = "gl_FragDepth";
// Only outputs for pixel shaders must be tracked.
if (Frequency == SF_Pixel)
{
for (auto& Output : CCHeader.Outputs)
{
// Handle targets.
if (Output.Name.StartsWith(TargetPrefix))
{
uint8 TargetIndex = ParseNumber(*Output.Name + TargetPrefix.Len());
Header.Bindings.InOutMask |= (1 << TargetIndex);
}
// Handle depth writes.
else if (Output.Name.Equals(GL_FragDepth))
{
Header.Bindings.InOutMask |= 0x8000;
}
}
}
bool bHasRegularUniformBuffers = false;
// Then 'normal' uniform buffers.
for (auto& UniformBlock : CCHeader.UniformBlocks)
{
uint16 UBIndex = UniformBlock.Index;
if (UBIndex >= Header.Bindings.NumUniformBuffers)
{
Header.Bindings.NumUniformBuffers = UBIndex + 1;
}
UsedUniformBufferSlots[UBIndex] = true;
ParameterMap.AddParameterAllocation(*UniformBlock.Name, UBIndex, 0, 0);
bHasRegularUniformBuffers = true;
}
// Packed global uniforms
const uint16 BytesPerComponent = 4;
TMap<ANSICHAR, uint16> PackedGlobalArraySize;
for (auto& PackedGlobal : CCHeader.PackedGlobals)
{
ParameterMap.AddParameterAllocation(
*PackedGlobal.Name,
PackedGlobal.PackedType,
PackedGlobal.Offset * BytesPerComponent,
PackedGlobal.Count * BytesPerComponent
);
uint16& Size = PackedGlobalArraySize.FindOrAdd(PackedGlobal.PackedType);
Size = FMath::Max<uint16>(BytesPerComponent * (PackedGlobal.Offset + PackedGlobal.Count), Size);
}
// Packed Uniform Buffers
TMap<int, TMap<ANSICHAR, uint16> > PackedUniformBuffersSize;
for (auto& PackedUB : CCHeader.PackedUBs)
{
check(PackedUB.Attribute.Index == Header.Bindings.NumUniformBuffers);
UsedUniformBufferSlots[PackedUB.Attribute.Index] = true;
ParameterMap.AddParameterAllocation(*PackedUB.Attribute.Name, Header.Bindings.NumUniformBuffers++, 0, 0);
// Nothing else...
//for (auto& Member : PackedUB.Members)
//{
//}
}
// Packed Uniform Buffers copy lists & setup sizes for each UB/Precision entry
for (auto& PackedUBCopy : CCHeader.PackedUBCopies)
{
CrossCompiler::FUniformBufferCopyInfo CopyInfo;
CopyInfo.SourceUBIndex = PackedUBCopy.SourceUB;
CopyInfo.SourceOffsetInFloats = PackedUBCopy.SourceOffset;
CopyInfo.DestUBIndex = PackedUBCopy.DestUB;
CopyInfo.DestUBTypeName = PackedUBCopy.DestPackedType;
CopyInfo.DestUBTypeIndex = CrossCompiler::PackedTypeNameToTypeIndex(CopyInfo.DestUBTypeName);
CopyInfo.DestOffsetInFloats = PackedUBCopy.DestOffset;
CopyInfo.SizeInFloats = PackedUBCopy.Count;
Header.UniformBuffersCopyInfo.Add(CopyInfo);
auto& UniformBufferSize = PackedUniformBuffersSize.FindOrAdd(CopyInfo.DestUBIndex);
uint16& Size = UniformBufferSize.FindOrAdd(CopyInfo.DestUBTypeName);
Size = FMath::Max<uint16>(BytesPerComponent * (CopyInfo.DestOffsetInFloats + CopyInfo.SizeInFloats), Size);
}
for (auto& PackedUBCopy : CCHeader.PackedUBGlobalCopies)
{
CrossCompiler::FUniformBufferCopyInfo CopyInfo;
CopyInfo.SourceUBIndex = PackedUBCopy.SourceUB;
CopyInfo.SourceOffsetInFloats = PackedUBCopy.SourceOffset;
CopyInfo.DestUBIndex = PackedUBCopy.DestUB;
CopyInfo.DestUBTypeName = PackedUBCopy.DestPackedType;
CopyInfo.DestUBTypeIndex = CrossCompiler::PackedTypeNameToTypeIndex(CopyInfo.DestUBTypeName);
CopyInfo.DestOffsetInFloats = PackedUBCopy.DestOffset;
CopyInfo.SizeInFloats = PackedUBCopy.Count;
Header.UniformBuffersCopyInfo.Add(CopyInfo);
uint16& Size = PackedGlobalArraySize.FindOrAdd(CopyInfo.DestUBTypeName);
Size = FMath::Max<uint16>(BytesPerComponent * (CopyInfo.DestOffsetInFloats + CopyInfo.SizeInFloats), Size);
}
Header.Bindings.bHasRegularUniformBuffers = bHasRegularUniformBuffers;
// 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);
CrossCompiler::FPackedArrayInfo Info;
Info.Size = Size;
Info.TypeName = TypeName;
Info.TypeIndex = CrossCompiler::PackedTypeNameToTypeIndex(TypeName);
Header.Bindings.PackedGlobalArrays.Add(Info);
}
// Setup Packed Uniform Buffers info
Header.Bindings.PackedUniformBuffers.Reserve(PackedUniformBuffersSize.Num());
for (auto Iterator = PackedUniformBuffersSize.CreateIterator(); Iterator; ++Iterator)
{
int BufferIndex = Iterator.Key();
auto& ArraySizes = Iterator.Value();
TArray<CrossCompiler::FPackedArrayInfo> InfoArray;
InfoArray.Reserve(ArraySizes.Num());
for (auto IterSizes = ArraySizes.CreateIterator(); IterSizes; ++IterSizes)
{
ANSICHAR TypeName = IterSizes.Key();
uint16 Size = IterSizes.Value();
Size = (Size + 0xf) & (~0xf);
CrossCompiler::FPackedArrayInfo Info;
Info.Size = Size;
Info.TypeName = TypeName;
Info.TypeIndex = CrossCompiler::PackedTypeNameToTypeIndex(TypeName);
InfoArray.Add(Info);
}
Header.Bindings.PackedUniformBuffers.Add(InfoArray);
}
// Then samplers.
TMap<FString, uint32> SamplerMap;
for (auto& Sampler : CCHeader.Samplers)
{
ParameterMap.AddParameterAllocation(
*Sampler.Name,
0,
Sampler.Offset,
Sampler.Count
);
Header.Bindings.NumSamplers = FMath::Max<uint8>(
Header.Bindings.NumSamplers,
Sampler.Offset + Sampler.Count
);
for (auto& SamplerState : Sampler.SamplerStates)
{
SamplerMap.Add(SamplerState, Sampler.Count);
}
}
// Then UAVs (images in Metal)
for (auto& UAV : CCHeader.UAVs)
{
ParameterMap.AddParameterAllocation(
*UAV.Name,
0,
UAV.Offset,
UAV.Count
);
Header.Bindings.NumUAVs = FMath::Max<uint8>(
Header.Bindings.NumSamplers,
UAV.Offset + UAV.Count
);
}
for (auto& SamplerState : CCHeader.SamplerStates)
{
ParameterMap.AddParameterAllocation(
*SamplerState.Name,
0,
SamplerState.Index,
SamplerMap[SamplerState.Name]
);
}
Header.NumThreadsX = CCHeader.NumThreads[0];
Header.NumThreadsY = CCHeader.NumThreads[1];
Header.NumThreadsZ = CCHeader.NumThreads[2];
// Build the SRT for this shader.
{
// Build the generic SRT for this shader.
FShaderResourceTable GenericSRT;
BuildResourceTableMapping(ShaderInput.Environment.ResourceTableMap, ShaderInput.Environment.ResourceTableLayoutHashes, UsedUniformBufferSlots, ShaderOutput.ParameterMap, GenericSRT);
// 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);
Header.Bindings.NumUniformBuffers = FMath::Max((uint8)GetNumUniformBuffersUsed(GenericSRT), Header.Bindings.NumUniformBuffers);
}
const int32 MaxSamplers = GetFeatureLevelMaxTextureSamplers(ERHIFeatureLevel::ES3_1);
Header.ShaderName = CCHeader.Name.GetCharArray();
if (Header.Bindings.NumSamplers > MaxSamplers)
{
ShaderOutput.bSucceeded = false;
FShaderCompilerError* NewError = new(ShaderOutput.Errors) FShaderCompilerError();
NewError->StrippedErrorMessage =
FString::Printf(TEXT("shader uses %d samplers exceeding the limit of %d"),
Header.Bindings.NumSamplers, MaxSamplers);
}
else if(ShaderInput.Environment.CompilerFlags.Contains(CFLAG_Debug))
{
// Write out the header and shader source code.
FMemoryWriter Ar(ShaderOutput.Code, true);
uint8 PrecompiledFlag = 0;
Ar << PrecompiledFlag;
Ar << Header;
Ar.Serialize((void*)USFSource, SourceLen + 1 - (USFSource - InShaderSource));
ShaderOutput.NumInstructions = 0;
ShaderOutput.NumTextureSamplers = Header.Bindings.NumSamplers;
ShaderOutput.bSucceeded = true;
}
else
{
// at this point, the shader source is ready to be compiled
FString InputFilename = FPaths::CreateTempFilename(*FPaths::EngineIntermediateDir(), TEXT("ShaderIn"), TEXT(""));
FString ObjFilename = InputFilename + TEXT(".o");
FString ArFilename = InputFilename + TEXT(".ar");
FString OutputFilename = InputFilename + TEXT(".lib");
InputFilename = InputFilename + TEXT(".metal");
// write out shader source
FFileHelper::SaveStringToFile(FString(USFSource), *InputFilename);
int32 ReturnCode = 0;
FString Results;
FString Errors;
bool bCompileAtRuntime = true;
bool bSucceeded = false;
#if PLATFORM_MAC
FString XcodePath = FPlatformMisc::GetXcodePath();
if (XcodePath.Len() > 0 && (bIsMobile || FPlatformMisc::MacOSXVersionCompare(10, 11, 0) >= 0))
{
FString MetalToolsPath = FString::Printf(TEXT("%s/Toolchains/XcodeDefault.xctoolchain/usr/bin"), *XcodePath);
FString MetalPath = MetalToolsPath + TEXT("/metal");
if (IFileManager::Get().FileSize(*MetalPath) <= 0)
{
MetalToolsPath = FString::Printf(TEXT("%s/Platforms/iPhoneOS.platform/usr/bin"), *XcodePath);
MetalPath = MetalToolsPath + TEXT("/metal");
}
// metal commandlines
FString Standard = (bIsMobile ? TEXT("-std=ios-metal1.0") : TEXT("-std=osx-metal1.1 -Wno-null-character -ffmast-math"));
FString Params = FString::Printf(TEXT("%s %s -o %s"), *Standard, *InputFilename, *ObjFilename);
FPlatformProcess::ExecProcess( *MetalPath, *Params, &ReturnCode, &Results, &Errors );
// handle compile error
if (ReturnCode != 0 || IFileManager::Get().FileSize(*ObjFilename) <= 0)
{
FShaderCompilerError* Error = new(OutErrors) FShaderCompilerError();
Error->ErrorFile = InputFilename;
Error->ErrorLineString = TEXT("0");
Error->StrippedErrorMessage = Results + Errors;
bSucceeded = false;
}
else
{
Params = FString::Printf(TEXT("r %s %s"), *ArFilename, *ObjFilename);
FString MetalArPath = MetalToolsPath + TEXT("/metal-ar");
FPlatformProcess::ExecProcess( *MetalArPath, *Params, &ReturnCode, &Results, &Errors );
// handle compile error
if (ReturnCode != 0 || IFileManager::Get().FileSize(*ArFilename) <= 0)
{
FShaderCompilerError* Error = new(OutErrors) FShaderCompilerError();
Error->ErrorFile = InputFilename;
Error->ErrorLineString = TEXT("0");
Error->StrippedErrorMessage = Results + Errors;
bSucceeded = false;
}
else
{
Params = FString::Printf(TEXT("-o %s %s"), *OutputFilename, *ArFilename);
FString MetalLibPath = MetalToolsPath + TEXT("/metallib");
FPlatformProcess::ExecProcess( *MetalLibPath, *Params, &ReturnCode, &Results, &Errors );
// handle compile error
if (ReturnCode != 0 || IFileManager::Get().FileSize(*OutputFilename) <= 0)
{
FShaderCompilerError* Error = new(OutErrors) FShaderCompilerError();
Error->ErrorFile = InputFilename;
Error->ErrorLineString = TEXT("0");
Error->StrippedErrorMessage = Results + Errors;
bSucceeded = false;
}
else
{
bCompileAtRuntime = false;
// Write out the header and compiled shader code
FMemoryWriter Ar(ShaderOutput.Code, true);
uint8 PrecompiledFlag = 1;
Ar << PrecompiledFlag;
Ar << Header;
// load output
TArray<uint8> CompiledShader;
FFileHelper::LoadFileToArray(CompiledShader, *OutputFilename);
// jam it into the output bytes
Ar.Serialize(CompiledShader.GetData(), CompiledShader.Num());
ShaderOutput.NumInstructions = 0;
ShaderOutput.NumTextureSamplers = Header.Bindings.NumSamplers;
ShaderOutput.bSucceeded = true;
bSucceeded = true;
}
}
}
}
#else
// Assume success for non-Mac
bSucceeded = true;
#endif // PLATFORM_MAC
if (bCompileAtRuntime)
{
// Write out the header and shader source code.
FMemoryWriter Ar(ShaderOutput.Code, true);
uint8 PrecompiledFlag = 0;
Ar << PrecompiledFlag;
Ar << Header;
Ar.Serialize((void*)USFSource, SourceLen + 1 - (USFSource - InShaderSource));
ShaderOutput.NumInstructions = 0;
ShaderOutput.NumTextureSamplers = Header.Bindings.NumSamplers;
ShaderOutput.bSucceeded = bSucceeded || ShaderOutput.bSucceeded;
}
else
{
IFileManager::Get().Delete(*InputFilename);
IFileManager::Get().Delete(*ObjFilename);
IFileManager::Get().Delete(*ArFilename);
IFileManager::Get().Delete(*OutputFilename);
}
}
}
/*------------------------------------------------------------------------------
External interface.
------------------------------------------------------------------------------*/
static FString CreateCommandLineHLSLCC( const FString& ShaderFile, const FString& OutputFile, const FString& EntryPoint, EHlslShaderFrequency Frequency, uint32 CCFlags )
{
const TCHAR* VersionSwitch = TEXT("-metal");
return CrossCompiler::CreateBatchFileContents(ShaderFile, OutputFile, Frequency, EntryPoint, VersionSwitch, CCFlags, TEXT(""));
}
void CompileShader_Metal(const FShaderCompilerInput& Input,FShaderCompilerOutput& Output,const FString& WorkingDirectory)
{
FString PreprocessedShader;
FShaderCompilerDefinitions AdditionalDefines;
EHlslCompileTarget HlslCompilerTarget = HCT_FeatureLevelES3_1;
AdditionalDefines.SetDefine(TEXT("COMPILER_HLSLCC"), 1 );
// @todo - Zebra - Work out which standard we need, this is dependent on the shader platform.
// For now only SP_METAL will compile for iOS, with MRT & SM5 only for Mac.
const bool bIsMobile = (Input.Target.Platform == SP_METAL);
if (bIsMobile)
{
AdditionalDefines.SetDefine(TEXT("IOS"), 1);
}
else
{
AdditionalDefines.SetDefine(TEXT("MAC"), 1);
}
AdditionalDefines.SetDefine(TEXT("row_major"), TEXT(""));
AdditionalDefines.SetDefine(TEXT("COMPILER_METAL"), 1);
static FName NAME_SF_METAL(TEXT("SF_METAL"));
static FName NAME_SF_METAL_MRT(TEXT("SF_METAL_MRT"));
static FName NAME_SF_METAL_SM5(TEXT("SF_METAL_SM5"));
if (Input.ShaderFormat == NAME_SF_METAL)
{
AdditionalDefines.SetDefine(TEXT("METAL_PROFILE"), 1);
}
else if (Input.ShaderFormat == NAME_SF_METAL_MRT)
{
AdditionalDefines.SetDefine(TEXT("METAL_MRT_PROFILE"), 1);
}
else if (Input.ShaderFormat == NAME_SF_METAL_SM5)
{
AdditionalDefines.SetDefine(TEXT("METAL_SM5_PROFILE"), 1);
}
else
{
Output.bSucceeded = false;
new(Output.Errors) FShaderCompilerError(*FString::Printf(TEXT("Invalid shader format '%s' passed to compiler."), *Input.ShaderFormat.ToString()));
return;
}
const bool bDumpDebugInfo = (Input.DumpDebugInfoPath != TEXT("") && IFileManager::Get().DirectoryExists(*Input.DumpDebugInfoPath));
if(Input.Environment.CompilerFlags.Contains(CFLAG_AvoidFlowControl))
{
AdditionalDefines.SetDefine(TEXT("COMPILER_SUPPORTS_ATTRIBUTES"), (uint32)1);
}
else
{
AdditionalDefines.SetDefine(TEXT("COMPILER_SUPPORTS_ATTRIBUTES"), (uint32)0);
}
if (PreprocessShader(PreprocessedShader, Output, Input, AdditionalDefines))
{
char* MetalShaderSource = NULL;
char* ErrorLog = NULL;
const EHlslShaderFrequency FrequencyTable[] =
{
HSF_VertexShader,
HSF_InvalidFrequency,
HSF_InvalidFrequency,
HSF_PixelShader,
HSF_InvalidFrequency,
HSF_ComputeShader
};
const EHlslShaderFrequency Frequency = FrequencyTable[Input.Target.Frequency];
if (Frequency == HSF_InvalidFrequency)
{
Output.bSucceeded = false;
FShaderCompilerError* NewError = new(Output.Errors) FShaderCompilerError();
NewError->StrippedErrorMessage = FString::Printf(
TEXT("%s shaders not supported for use in Metal."),
CrossCompiler::GetFrequencyName((EShaderFrequency)Input.Target.Frequency)
);
return;
}
// This requires removing the HLSLCC_NoPreprocess flag later on!
if (!RemoveUniformBuffersFromSource(PreprocessedShader))
{
return;
}
// Write out the preprocessed file and a batch file to compile it if requested (DumpDebugInfoPath is valid)
if (bDumpDebugInfo)
{
FArchive* FileWriter = IFileManager::Get().CreateFileWriter(*(Input.DumpDebugInfoPath / Input.SourceFilename + TEXT(".usf")));
if (FileWriter)
{
auto AnsiSourceFile = StringCast<ANSICHAR>(*PreprocessedShader);
FileWriter->Serialize((ANSICHAR*)AnsiSourceFile.Get(), AnsiSourceFile.Length());
FileWriter->Close();
delete FileWriter;
}
}
uint32 CCFlags = HLSLCC_NoPreprocess | HLSLCC_PackUniforms;
if (!bIsMobile)
{
CCFlags |= HLSLCC_FixAtomicReferences;
}
//CCFlags |= HLSLCC_FlattenUniformBuffers | HLSLCC_FlattenUniformBufferStructures;
if (bDumpDebugInfo)
{
const FString MetalFile = (Input.DumpDebugInfoPath / TEXT("Output.metal"));
const FString USFFile = (Input.DumpDebugInfoPath / Input.SourceFilename) + TEXT(".usf");
const FString CCBatchFileContents = CreateCommandLineHLSLCC(USFFile, MetalFile, *Input.EntryPointName, Frequency, CCFlags);
if (!CCBatchFileContents.IsEmpty())
{
FFileHelper::SaveStringToFile(CCBatchFileContents, *(Input.DumpDebugInfoPath / TEXT("CrossCompile.bat")));
}
}
// Required as we added the RemoveUniformBuffersFromSource() function (the cross-compiler won't be able to interpret comments w/o a preprocessor)
CCFlags &= ~HLSLCC_NoPreprocess;
FMetalCodeBackend MetalBackEnd(CCFlags, HlslCompilerTarget);
FMetalLanguageSpec MetalLanguageSpec;
int32 Result = 0;
FHlslCrossCompilerContext CrossCompilerContext(CCFlags, Frequency, HlslCompilerTarget);
if (CrossCompilerContext.Init(TCHAR_TO_ANSI(*Input.SourceFilename), &MetalLanguageSpec))
{
Result = CrossCompilerContext.Run(
TCHAR_TO_ANSI(*PreprocessedShader),
TCHAR_TO_ANSI(*Input.EntryPointName),
&MetalBackEnd,
&MetalShaderSource,
&ErrorLog
) ? 1 : 0;
}
int32 SourceLen = MetalShaderSource ? FCStringAnsi::Strlen(MetalShaderSource) : 0;
if(MetalShaderSource)
{
uint32 Len = FCStringAnsi::Strlen(TCHAR_TO_ANSI(*Input.SourceFilename)) + FCStringAnsi::Strlen(TCHAR_TO_ANSI(*Input.EntryPointName)) + FCStringAnsi::Strlen(MetalShaderSource) + 20;
char* Dest = (char*)malloc(Len);
FCStringAnsi::Snprintf(Dest, Len, "// ! %s.usf:%s\n%s", (const char*)TCHAR_TO_ANSI(*Input.SourceFilename), (const char*)TCHAR_TO_ANSI(*Input.EntryPointName), (const char*)MetalShaderSource);
free(MetalShaderSource);
MetalShaderSource = Dest;
SourceLen = FCStringAnsi::Strlen(MetalShaderSource);
}
if (bDumpDebugInfo)
{
if (SourceLen > 0)
{
FArchive* FileWriter = IFileManager::Get().CreateFileWriter(*(Input.DumpDebugInfoPath / Input.SourceFilename + TEXT(".metal")));
if (FileWriter)
{
FileWriter->Serialize(MetalShaderSource, SourceLen + 1);
FileWriter->Close();
delete FileWriter;
}
}
}
if (Result != 0)
{
Output.Target = Input.Target;
BuildMetalShaderOutput(Output, Input, MetalShaderSource, SourceLen, bIsMobile, Output.Errors);
}
else
{
FString Tmp = ANSI_TO_TCHAR(ErrorLog);
TArray<FString> ErrorLines;
Tmp.ParseIntoArray(ErrorLines, TEXT("\n"), true);
for (int32 LineIndex = 0; LineIndex < ErrorLines.Num(); ++LineIndex)
{
const FString& Line = ErrorLines[LineIndex];
CrossCompiler::ParseHlslccError(Output.Errors, Line);
}
}
if (MetalShaderSource)
{
free(MetalShaderSource);
}
if (ErrorLog)
{
free(ErrorLog);
}
}
}