コード例 #1
0
	bool FHlslccHeader::Read(const ANSICHAR*& ShaderSource, int32 SourceLen)
	{
#define DEF_PREFIX_STR(Str) \
		static const ANSICHAR* Str##Prefix = "// @" #Str ": "; \
		static const int32 Str##PrefixLen = FCStringAnsi::Strlen(Str##Prefix)
		DEF_PREFIX_STR(Inputs);
		DEF_PREFIX_STR(Outputs);
		DEF_PREFIX_STR(UniformBlocks);
		DEF_PREFIX_STR(Uniforms);
		DEF_PREFIX_STR(PackedGlobals);
		DEF_PREFIX_STR(PackedUB);
		DEF_PREFIX_STR(PackedUBCopies);
		DEF_PREFIX_STR(PackedUBGlobalCopies);
		DEF_PREFIX_STR(Samplers);
		DEF_PREFIX_STR(UAVs);
		DEF_PREFIX_STR(SamplerStates);
		DEF_PREFIX_STR(NumThreads);
#undef DEF_PREFIX_STR

		// Skip any comments that come before the signature.
		while (FCStringAnsi::Strncmp(ShaderSource, "//", 2) == 0 &&
			FCStringAnsi::Strncmp(ShaderSource + 2, " !", 2) != 0 &&
			FCStringAnsi::Strncmp(ShaderSource + 2, " @", 2) != 0)
		{
			ShaderSource += 2;
			while (*ShaderSource && *ShaderSource++ != '\n')
			{
				// Do nothing
			}
		}

		// Read shader name if any
		if (FCStringAnsi::Strncmp(ShaderSource, "// !", 4) == 0)
		{
			ShaderSource += 4;
			while (*ShaderSource && *ShaderSource != '\n')
			{
				Name += (TCHAR)*ShaderSource;
				++ShaderSource;
			}

			if (*ShaderSource == '\n')
			{
				++ShaderSource;
			}
		}

		// Skip any comments that come before the signature.
		while (FCStringAnsi::Strncmp(ShaderSource, "//", 2) == 0 &&
			FCStringAnsi::Strncmp(ShaderSource + 2, " @", 2) != 0)
		{
			ShaderSource += 2;
			while (*ShaderSource && *ShaderSource++ != '\n')
			{
				// Do nothing
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, InputsPrefix, InputsPrefixLen) == 0)
		{
			ShaderSource += InputsPrefixLen;

			if (!ReadInOut(ShaderSource, Inputs))
			{
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, OutputsPrefix, OutputsPrefixLen) == 0)
		{
			ShaderSource += OutputsPrefixLen;

			if (!ReadInOut(ShaderSource, Outputs))
			{
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, UniformBlocksPrefix, UniformBlocksPrefixLen) == 0)
		{
			ShaderSource += UniformBlocksPrefixLen;

			while (*ShaderSource && *ShaderSource != '\n')
			{
				FAttribute UniformBlock;
				if (!ParseIdentifier(ShaderSource, UniformBlock.Name))
				{
					return false;
				}

				if (!Match(ShaderSource, '('))
				{
					return false;
				}
				
				if (!ParseIntegerNumber(ShaderSource, UniformBlock.Index))
				{
					return false;
				}

				if (!Match(ShaderSource, ')'))
				{
					return false;
				}

				UniformBlocks.Add(UniformBlock);

				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				if (Match(ShaderSource, ','))
				{
					continue;
				}
			
				//#todo-rco: Need a log here
				//UE_LOG(ShaderCompilerCommon, Warning, TEXT("Invalid char '%c'"), *ShaderSource);
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, UniformsPrefix, UniformsPrefixLen) == 0)
		{
			// @todo-mobile: Will we ever need to support this code path?
			check(0);
			return false;
/*
			ShaderSource += UniformsPrefixLen;

			while (*ShaderSource && *ShaderSource != '\n')
			{
				uint16 ArrayIndex = 0;
				uint16 Offset = 0;
				uint16 NumComponents = 0;

				FString ParameterName = ParseIdentifier(ShaderSource);
				verify(ParameterName.Len() > 0);
				verify(Match(ShaderSource, '('));
				ArrayIndex = ParseNumber(ShaderSource);
				verify(Match(ShaderSource, ':'));
				Offset = ParseNumber(ShaderSource);
				verify(Match(ShaderSource, ':'));
				NumComponents = ParseNumber(ShaderSource);
				verify(Match(ShaderSource, ')'));

				ParameterMap.AddParameterAllocation(
					*ParameterName,
					ArrayIndex,
					Offset * BytesPerComponent,
					NumComponents * BytesPerComponent
					);

				if (ArrayIndex < OGL_NUM_PACKED_UNIFORM_ARRAYS)
				{
					PackedUniformSize[ArrayIndex] = FMath::Max<uint16>(
						PackedUniformSize[ArrayIndex],
						BytesPerComponent * (Offset + NumComponents)
						);
				}

				// Skip the comma.
				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				verify(Match(ShaderSource, ','));
			}

			Match(ShaderSource, '\n');
*/
		}

		// @PackedGlobals: Global0(h:0,1),Global1(h:4,1),Global2(h:8,1)
		if (FCStringAnsi::Strncmp(ShaderSource, PackedGlobalsPrefix, PackedGlobalsPrefixLen) == 0)
		{
			ShaderSource += PackedGlobalsPrefixLen;
			while (*ShaderSource && *ShaderSource != '\n')
			{
				FPackedGlobal PackedGlobal;
				if (!ParseIdentifier(ShaderSource, PackedGlobal.Name))
				{
					return false;
				}

				if (!Match(ShaderSource, '('))
				{
					return false;
				}

				PackedGlobal.PackedType = *ShaderSource++;

				if (!Match(ShaderSource, ':'))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, PackedGlobal.Offset))
				{
					return false;
				}

				if (!Match(ShaderSource, ','))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, PackedGlobal.Count))
				{
					return false;
				}

				if (!Match(ShaderSource, ')'))
				{
					return false;
				}

				PackedGlobals.Add(PackedGlobal);

				// Break if EOL
				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				// Has to be a comma!
				if (Match(ShaderSource, ','))
				{
					continue;
				}

				//#todo-rco: Need a log here
				//UE_LOG(ShaderCompilerCommon, Warning, TEXT("Invalid char '%c'"), *ShaderSource);
				return false;
			}
		}

		// Packed Uniform Buffers (Multiple lines)
		// @PackedUB: CBuffer(0): CBMember0(0,1),CBMember1(1,1)
		while (FCStringAnsi::Strncmp(ShaderSource, PackedUBPrefix, PackedUBPrefixLen) == 0)
		{
			ShaderSource += PackedUBPrefixLen;

			FPackedUB PackedUB;

			if (!ParseIdentifier(ShaderSource, PackedUB.Attribute.Name))
			{
				return false;
			}

			if (!Match(ShaderSource, '('))
			{
				return false;
			}
			
			if (!ParseIntegerNumber(ShaderSource, PackedUB.Attribute.Index))
			{
				return false;
			}

			if (!Match(ShaderSource, ')'))
			{
				return false;
			}

			if (!Match(ShaderSource, ':'))
			{
				return false;
			}

			if (!Match(ShaderSource, ' '))
			{
				return false;
			}

			while (*ShaderSource && *ShaderSource != '\n')
			{
				FPackedUB::FMember Member;
				ParseIdentifier(ShaderSource, Member.Name);
				if (!Match(ShaderSource, '('))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, Member.Offset))
				{
					return false;
				}
				
				if (!Match(ShaderSource, ','))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, Member.Count))
				{
					return false;
				}

				if (!Match(ShaderSource, ')'))
				{
					return false;
				}

				PackedUB.Members.Add(Member);

				// Break if EOL
				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				// Has to be a comma!
				if (Match(ShaderSource, ','))
				{
					continue;
				}

				//#todo-rco: Need a log here
				//UE_LOG(ShaderCompilerCommon, Warning, TEXT("Invalid char '%c'"), *ShaderSource);
				return false;
			}

			PackedUBs.Add(PackedUB);
		}

		// @PackedUBCopies: 0:0-0:h:0:1,0:1-0:h:4:1,1:0-1:h:0:1
		if (FCStringAnsi::Strncmp(ShaderSource, PackedUBCopiesPrefix, PackedUBCopiesPrefixLen) == 0)
		{
			ShaderSource += PackedUBCopiesPrefixLen;
			if (!ReadCopies(ShaderSource, false, PackedUBCopies))
			{
				return false;
			}
		}

		// @PackedUBGlobalCopies: 0:0-h:12:1,0:1-h:16:1,1:0-h:20:1
		if (FCStringAnsi::Strncmp(ShaderSource, PackedUBGlobalCopiesPrefix, PackedUBGlobalCopiesPrefixLen) == 0)
		{
			ShaderSource += PackedUBGlobalCopiesPrefixLen;
			if (!ReadCopies(ShaderSource, true, PackedUBGlobalCopies))
			{
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, SamplersPrefix, SamplersPrefixLen) == 0)
		{
			ShaderSource += SamplersPrefixLen;

			while (*ShaderSource && *ShaderSource != '\n')
			{
				FSampler Sampler;

				if (!ParseIdentifier(ShaderSource, Sampler.Name))
				{
					return false;
				}

				if (!Match(ShaderSource, '('))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, Sampler.Offset))
				{
					return false;
				}

				if (!Match(ShaderSource, ':'))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, Sampler.Count))
				{
					return false;
				}

				if (Match(ShaderSource, '['))
				{
					// Sampler States
					do
					{
						FString SamplerState;
						
						if (!ParseIdentifier(ShaderSource, SamplerState))
						{
							return false;
						}

						Sampler.SamplerStates.Add(SamplerState);
					}
					while (Match(ShaderSource, ','));

					if (!Match(ShaderSource, ']'))
					{
						return false;
					}
				}

				if (!Match(ShaderSource, ')'))
				{
					return false;
				}

				Samplers.Add(Sampler);

				// Break if EOL
				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				// Has to be a comma!
				if (Match(ShaderSource, ','))
				{
					continue;
				}

				//#todo-rco: Need a log here
				//UE_LOG(ShaderCompilerCommon, Warning, TEXT("Invalid char '%c'"), *ShaderSource);
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, UAVsPrefix, UAVsPrefixLen) == 0)
		{
			ShaderSource += UAVsPrefixLen;

			while (*ShaderSource && *ShaderSource != '\n')
			{
				FUAV UAV;

				if (!ParseIdentifier(ShaderSource, UAV.Name))
				{
					return false;
				}

				if (!Match(ShaderSource, '('))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, UAV.Offset))
				{
					return false;
				}

				if (!Match(ShaderSource, ':'))
				{
					return false;
				}

				if (!ParseIntegerNumber(ShaderSource, UAV.Count))
				{
					return false;
				}

				if (!Match(ShaderSource, ')'))
				{
					return false;
				}

				UAVs.Add(UAV);

				// Break if EOL
				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				// Has to be a comma!
				if (Match(ShaderSource, ','))
				{
					continue;
				}

				//#todo-rco: Need a log here
				//UE_LOG(ShaderCompilerCommon, Warning, TEXT("Invalid char '%c'"), *ShaderSource);
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, SamplerStatesPrefix, SamplerStatesPrefixLen) == 0)
		{
			ShaderSource += SamplerStatesPrefixLen;
			while (*ShaderSource && *ShaderSource != '\n')
			{
				FAttribute SamplerState;
				if (!ParseIntegerNumber(ShaderSource, SamplerState.Index))
				{
					return false;
				}

				if (!Match(ShaderSource, ':'))
				{
					return false;
				}

				if (!ParseIdentifier(ShaderSource, SamplerState.Name))
				{
					return false;
				}

				SamplerStates.Add(SamplerState);

				// Break if EOL
				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				// Has to be a comma!
				if (Match(ShaderSource, ','))
				{
					continue;
				}

				//#todo-rco: Need a log here
				//UE_LOG(ShaderCompilerCommon, Warning, TEXT("Invalid char '%c'"), *ShaderSource);
				return false;
			}
		}

		if (FCStringAnsi::Strncmp(ShaderSource, NumThreadsPrefix, NumThreadsPrefixLen) == 0)
		{
			ShaderSource += NumThreadsPrefixLen;
			if (!ParseIntegerNumber(ShaderSource, NumThreads[0]))
			{
				return false;
			}
			if (!Match(ShaderSource, ','))
			{
				return false;
			}

			if (!Match(ShaderSource, ' '))
			{
				return false;
			}

			if (!ParseIntegerNumber(ShaderSource, NumThreads[1]))
			{
				return false;
			}

			if (!Match(ShaderSource, ','))
			{
				return false;
			}

			if (!Match(ShaderSource, ' '))
			{
				return false;
			}

			if (!ParseIntegerNumber(ShaderSource, NumThreads[2]))
			{
				return false;
			}

			if (!Match(ShaderSource, '\n'))
			{
				return false;
			}
		}

		return true;
	}
コード例 #2
0
/**
 * 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,
	TArray<FShaderCompilerError>& OutErrors
	)
{
	FMetalCodeHeader Header = {0};
	const ANSICHAR* ShaderSource = InShaderSource;
	FShaderParameterMap& ParameterMap = ShaderOutput.ParameterMap;
	EShaderFrequency Frequency = (EShaderFrequency)ShaderOutput.Target.Frequency;

	TBitArray<> UsedUniformBufferSlots;
	UsedUniformBufferSlots.Init(false,32);

	// Write out the magic markers.
	Header.Frequency = Frequency;

	#define DEF_PREFIX_STR(Str) \
		const ANSICHAR* Str##Prefix = "// @" #Str ": "; \
		const int32 Str##PrefixLen = FCStringAnsi::Strlen(Str##Prefix)
	DEF_PREFIX_STR(Inputs);
	DEF_PREFIX_STR(Outputs);
	DEF_PREFIX_STR(UniformBlocks);
	DEF_PREFIX_STR(Uniforms);
	DEF_PREFIX_STR(PackedGlobals);
	DEF_PREFIX_STR(PackedUB);
	DEF_PREFIX_STR(PackedUBCopies);
	DEF_PREFIX_STR(PackedUBGlobalCopies);
	DEF_PREFIX_STR(Samplers);
	DEF_PREFIX_STR(UAVs);
	DEF_PREFIX_STR(SamplerStates);
	DEF_PREFIX_STR(NumThreads);
	#undef DEF_PREFIX_STR

	// Skip any comments that come before the signature.
	while (	FCStringAnsi::Strncmp(ShaderSource, "//", 2) == 0 &&
			FCStringAnsi::Strncmp(ShaderSource, "// @", 4) != 0 )
	{
		while (*ShaderSource && *ShaderSource++ != '\n') {}
	}

	// HLSLCC first prints the list of inputs.
	if (FCStringAnsi::Strncmp(ShaderSource, InputsPrefix, InputsPrefixLen) == 0)
	{
		ShaderSource += InputsPrefixLen;

		// Only inputs for vertex shaders must be tracked.
		if (Frequency == SF_Vertex)
		{
			const ANSICHAR* AttributePrefix = "in_ATTRIBUTE";
			const int32 AttributePrefixLen = FCStringAnsi::Strlen(AttributePrefix);
			while (*ShaderSource && *ShaderSource != '\n')
			{
				// Skip the type.
				while (*ShaderSource && *ShaderSource++ != ':') {}
				
				// Only process attributes.
				if (FCStringAnsi::Strncmp(ShaderSource, AttributePrefix, AttributePrefixLen) == 0)
				{
					ShaderSource += AttributePrefixLen;
					uint8 AttributeIndex = ParseNumber(ShaderSource);
					Header.Bindings.InOutMask |= (1 << AttributeIndex);
				}

				// Skip to the next.
				while (*ShaderSource && *ShaderSource != ',' && *ShaderSource != '\n')
				{
					ShaderSource++;
				}

				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				verify(Match(ShaderSource, ','));
			}
		}
		else
		{
			// Skip to the next line.
			while (*ShaderSource && *ShaderSource++ != '\n') {}
		}
	}

	// Then the list of outputs.
	if (FCStringAnsi::Strncmp(ShaderSource, OutputsPrefix, OutputsPrefixLen) == 0)
	{
		ShaderSource += OutputsPrefixLen;

		// Only outputs for pixel shaders must be tracked.
		if (Frequency == SF_Pixel)
		{
			const ANSICHAR* TargetPrefix = "out_Target";
			const int32 TargetPrefixLen = FCStringAnsi::Strlen(TargetPrefix);

			while (*ShaderSource && *ShaderSource != '\n')
			{
				// Skip the type.
				while (*ShaderSource && *ShaderSource++ != ':') {}

				// Handle targets.
				if (FCStringAnsi::Strncmp(ShaderSource, TargetPrefix, TargetPrefixLen) == 0)
				{
					ShaderSource += TargetPrefixLen;
					uint8 TargetIndex = ParseNumber(ShaderSource);
					Header.Bindings.InOutMask |= (1 << TargetIndex);
				}
				// Handle depth writes.
				else if (FCStringAnsi::Strcmp(ShaderSource, "gl_FragDepth") == 0)
				{
					Header.Bindings.InOutMask |= 0x8000;
				}

				// Skip to the next.
				while (*ShaderSource && *ShaderSource != ',' && *ShaderSource != '\n')
				{
					ShaderSource++;
				}

				if (Match(ShaderSource, '\n'))
				{
					break;
				}

				verify(Match(ShaderSource, ','));
			}
		}
		else
		{
			// Skip to the next line.
			while (*ShaderSource && *ShaderSource++ != '\n') {}
		}
	}

	bool bHasRegularUniformBuffers = false;

	// Then 'normal' uniform buffers.
	if (FCStringAnsi::Strncmp(ShaderSource, UniformBlocksPrefix, UniformBlocksPrefixLen) == 0)
	{
		ShaderSource += UniformBlocksPrefixLen;

		while (*ShaderSource && *ShaderSource != '\n')
		{
			FString BufferName = ParseIdentifier(ShaderSource);
			verify(BufferName.Len() > 0);
			verify(Match(ShaderSource, '('));
			uint16 UBIndex = ParseNumber(ShaderSource);
			if (UBIndex >= Header.Bindings.NumUniformBuffers)
			{
				Header.Bindings.NumUniformBuffers = UBIndex + 1;
			}
			UsedUniformBufferSlots[UBIndex] = true;
			verify(Match(ShaderSource, ')'));
			ParameterMap.AddParameterAllocation(*BufferName, UBIndex, 0, 0);
			bHasRegularUniformBuffers = true;

			// Skip the comma.
			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			verify(Match(ShaderSource, ','));
		}

		Match(ShaderSource, '\n');
	}

	// Then uniforms.
	const uint16 BytesPerComponent = 4;
/*
	uint16 PackedUniformSize[OGL_NUM_PACKED_UNIFORM_ARRAYS] = {0};
	FMemory::Memzero(&PackedUniformSize, sizeof(PackedUniformSize));
*/
	if (FCStringAnsi::Strncmp(ShaderSource, UniformsPrefix, UniformsPrefixLen) == 0)
	{
		// @todo-mobile: Will we ever need to support this code path?
		check(0);
/*
		ShaderSource += UniformsPrefixLen;

		while (*ShaderSource && *ShaderSource != '\n')
		{
			uint16 ArrayIndex = 0;
			uint16 Offset = 0;
			uint16 NumComponents = 0;

			FString ParameterName = ParseIdentifier(ShaderSource);
			verify(ParameterName.Len() > 0);
			verify(Match(ShaderSource, '('));
			ArrayIndex = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));
			Offset = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));
			NumComponents = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ')'));

			ParameterMap.AddParameterAllocation(
				*ParameterName,
				ArrayIndex,
				Offset * BytesPerComponent,
				NumComponents * BytesPerComponent
				);

			if (ArrayIndex < OGL_NUM_PACKED_UNIFORM_ARRAYS)
			{
				PackedUniformSize[ArrayIndex] = FMath::Max<uint16>(
					PackedUniformSize[ArrayIndex],
					BytesPerComponent * (Offset + NumComponents)
					);
			}

			// Skip the comma.
			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			verify(Match(ShaderSource, ','));
		}

		Match(ShaderSource, '\n');
*/
	}

	// Packed global uniforms
	TMap<ANSICHAR, uint16> PackedGlobalArraySize;
	if (FCStringAnsi::Strncmp(ShaderSource, PackedGlobalsPrefix, PackedGlobalsPrefixLen) == 0)
	{
		ShaderSource += PackedGlobalsPrefixLen;
		while (*ShaderSource && *ShaderSource != '\n')
		{
			ANSICHAR ArrayIndex = 0;
			uint16 Offset = 0;
			uint16 NumComponents = 0;

			FString ParameterName = ParseIdentifier(ShaderSource);
			verify(ParameterName.Len() > 0);
			verify(Match(ShaderSource, '('));
			ArrayIndex = *ShaderSource++;
			verify(Match(ShaderSource, ':'));
			Offset = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ','));
			NumComponents = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ')'));

			ParameterMap.AddParameterAllocation(
				*ParameterName,
				ArrayIndex,
				Offset * BytesPerComponent,
				NumComponents * BytesPerComponent
				);

			uint16& Size = PackedGlobalArraySize.FindOrAdd(ArrayIndex);
			Size = FMath::Max<uint16>(BytesPerComponent * (Offset + NumComponents), Size);

			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			// Skip the comma.
			verify(Match(ShaderSource, ','));
		}

		Match(ShaderSource, '\n');
	}

	// Packed Uniform Buffers
	TMap<int, TMap<ANSICHAR, uint16> > PackedUniformBuffersSize;
	while (FCStringAnsi::Strncmp(ShaderSource, PackedUBPrefix, PackedUBPrefixLen) == 0)
	{
		ShaderSource += PackedUBPrefixLen;
		FString BufferName = ParseIdentifier(ShaderSource);
		verify(BufferName.Len() > 0);
		verify(Match(ShaderSource, '('));
		uint16 BufferIndex = ParseNumber(ShaderSource);
		check(BufferIndex == Header.Bindings.NumUniformBuffers);
		verify(Match(ShaderSource, ')'));
		ParameterMap.AddParameterAllocation(*BufferName, Header.Bindings.NumUniformBuffers++, 0, 0);

		verify(Match(ShaderSource, ':'));
		Match(ShaderSource, ' ');
		while (*ShaderSource && *ShaderSource != '\n')
		{
			FString ParameterName = ParseIdentifier(ShaderSource);
			verify(ParameterName.Len() > 0);
			verify(Match(ShaderSource, '('));
			ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ','));
			ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ')'));

			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			verify(Match(ShaderSource, ','));
		}
	}

	// Packed Uniform Buffers copy lists & setup sizes for each UB/Precision entry
	if (FCStringAnsi::Strncmp(ShaderSource, PackedUBCopiesPrefix, PackedUBCopiesPrefixLen) == 0)
	{
		ShaderSource += PackedUBCopiesPrefixLen;
		while (*ShaderSource && *ShaderSource != '\n')
		{
			FMetalUniformBufferCopyInfo CopyInfo;

			CopyInfo.SourceUBIndex = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));

			CopyInfo.SourceOffsetInFloats = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, '-'));

			CopyInfo.DestUBIndex = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));

			CopyInfo.DestUBTypeName = *ShaderSource++;
			CopyInfo.DestUBTypeIndex = CrossCompiler::PackedTypeNameToTypeIndex(CopyInfo.DestUBTypeName);
			verify(Match(ShaderSource, ':'));

			CopyInfo.DestOffsetInFloats = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));

			CopyInfo.SizeInFloats = ParseNumber(ShaderSource);

			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);

			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			verify(Match(ShaderSource, ','));
		}
	}

	if (FCStringAnsi::Strncmp(ShaderSource, PackedUBGlobalCopiesPrefix, PackedUBGlobalCopiesPrefixLen) == 0)
	{
		ShaderSource += PackedUBGlobalCopiesPrefixLen;
		while (*ShaderSource && *ShaderSource != '\n')
		{
			FMetalUniformBufferCopyInfo CopyInfo;

			CopyInfo.SourceUBIndex = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));

			CopyInfo.SourceOffsetInFloats = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, '-'));

			CopyInfo.DestUBIndex = 0;

			CopyInfo.DestUBTypeName = *ShaderSource++;
			CopyInfo.DestUBTypeIndex = CrossCompiler::PackedTypeNameToTypeIndex(CopyInfo.DestUBTypeName);
			verify(Match(ShaderSource, ':'));

			CopyInfo.DestOffsetInFloats = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));

			CopyInfo.SizeInFloats = ParseNumber(ShaderSource);

			Header.UniformBuffersCopyInfo.Add(CopyInfo);

			uint16& Size = PackedGlobalArraySize.FindOrAdd(CopyInfo.DestUBTypeName);
			Size = FMath::Max<uint16>(BytesPerComponent * (CopyInfo.DestOffsetInFloats + CopyInfo.SizeInFloats), Size);

			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			verify(Match(ShaderSource, ','));
		}
	}
	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.
	if (FCStringAnsi::Strncmp(ShaderSource, SamplersPrefix, SamplersPrefixLen) == 0)
	{
		ShaderSource += SamplersPrefixLen;

		while (*ShaderSource && *ShaderSource != '\n')
		{
			uint16 Offset = 0;
			uint16 NumSamplers = 0;

			FString ParameterName = ParseIdentifier(ShaderSource);
			verify(ParameterName.Len() > 0);
			verify(Match(ShaderSource, '('));
			Offset = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));
			NumSamplers = ParseNumber(ShaderSource);
			ParameterMap.AddParameterAllocation(
				*ParameterName,
				0,
				Offset,
				NumSamplers
				);

			Header.Bindings.NumSamplers = FMath::Max<uint8>(
				Header.Bindings.NumSamplers,
				Offset + NumSamplers
				);

			if (Match(ShaderSource, '['))
			{
				// Sampler States
				do
				{
					FString SamplerState = ParseIdentifier(ShaderSource);
					checkSlow(SamplerState.Len() != 0);
					ParameterMap.AddParameterAllocation(
						*SamplerState,
						0,
						Offset,
						NumSamplers
						);
				}
				while (Match(ShaderSource, ','));
				verify(Match(ShaderSource, ']'));
			}

			verify(Match(ShaderSource, ')'));

			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			// Skip the comma.
			verify(Match(ShaderSource, ','));
		}
	}	

	// Then UAVs (images in Metal)
	if (FCStringAnsi::Strncmp(ShaderSource, UAVsPrefix, UAVsPrefixLen) == 0)
	{
		ShaderSource += UAVsPrefixLen;

		while (*ShaderSource && *ShaderSource != '\n')
		{
			uint16 Offset = 0;
			uint16 NumUAVs = 0;

			FString ParameterName = ParseIdentifier(ShaderSource);
			verify(ParameterName.Len() > 0);
			verify(Match(ShaderSource, '('));
			Offset = ParseNumber(ShaderSource);
			verify(Match(ShaderSource, ':'));
			NumUAVs = ParseNumber(ShaderSource);

			ParameterMap.AddParameterAllocation(
				*ParameterName,
				0,
				Offset,
				NumUAVs
				);

			Header.Bindings.NumUAVs = FMath::Max<uint8>(
				Header.Bindings.NumUAVs,
				Offset + NumUAVs
				);

			verify(Match(ShaderSource, ')'));

			if (Match(ShaderSource, '\n'))
			{
				break;
			}

			// Skip the comma.
			verify(Match(ShaderSource, ','));
		}
	}

	if (FCStringAnsi::Strncmp(ShaderSource, NumThreadsPrefix, NumThreadsPrefixLen) == 0)
	{
		ShaderSource += NumThreadsPrefixLen;
		Header.NumThreadsX = ParseNumber(ShaderSource);
		verify(Match(ShaderSource, ','));
		Match(ShaderSource, ' ');
		Header.NumThreadsY = ParseNumber(ShaderSource);
		verify(Match(ShaderSource, ','));
		Match(ShaderSource, ' ');
		Header.NumThreadsZ = ParseNumber(ShaderSource);
		verify(Match(ShaderSource, '\n'));
	}

	// 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);

	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 METAL_OFFLINE_COMPILE
		// 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(ShaderSource), *InputFilename);
		
		int32 ReturnCode = 0;
		FString Results;
		FString Errors;
		bool bHadError = true;

		// metal commandlines
		FString Params = FString::Printf(TEXT("-std=ios-metal1.0 %s -o %s"), *InputFilename, *ObjFilename);
		FPlatformProcess::ExecProcess( TEXT("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/bin/metal"), *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;
		}
		else
		{
			Params = FString::Printf(TEXT("r %s %s"), *ArFilename, *ObjFilename);
			FPlatformProcess::ExecProcess( TEXT("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/bin/metal-ar"), *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;
			}
			else
			{
				Params = FString::Printf(TEXT("-o %s %s"), *OutputFilename, *ArFilename);
				FPlatformProcess::ExecProcess( TEXT("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/bin/metallib"), *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;
				}
				else
				{
					bHadError = 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;
				}
			}
		}
		
		if (bHadError)
		{
			// Write out the header and shader source code.
			FMemoryWriter Ar(ShaderOutput.Code, true);
			uint8 PrecompiledFlag = 0;
			Ar << PrecompiledFlag;
			Ar << Header;
			Ar.Serialize((void*)ShaderSource, SourceLen + 1 - (ShaderSource - InShaderSource));
			
			ShaderOutput.NumInstructions = 0;
			ShaderOutput.NumTextureSamplers = Header.Bindings.NumSamplers;
			ShaderOutput.bSucceeded = true;
		}
		
		IFileManager::Get().Delete(*InputFilename);
		IFileManager::Get().Delete(*ObjFilename);
		IFileManager::Get().Delete(*ArFilename);
		IFileManager::Get().Delete(*OutputFilename);
#else
		// Write out the header and shader source code.
		FMemoryWriter Ar(ShaderOutput.Code, true);
		uint8 PrecompiledFlag = 0;
		Ar << PrecompiledFlag;
		Ar << Header;
		Ar.Serialize((void*)ShaderSource, SourceLen + 1 - (ShaderSource - InShaderSource));
		
		ShaderOutput.NumInstructions = 0;
		ShaderOutput.NumTextureSamplers = Header.Bindings.NumSamplers;
		ShaderOutput.bSucceeded = true;
#endif
	}
}