コード例 #1
0
/**
 * Construct the final microcode from the compiled and verified shader source.
 * @param ShaderOutput - Where to store the microcode and parameter map.
 * @param InShaderSource - GLSL source with input/output signature.
 * @param SourceLen - The length of the GLSL source code.
 */
static void BuildShaderOutput(
	FShaderCompilerOutput& ShaderOutput,
	const FShaderCompilerInput& ShaderInput, 
	const ANSICHAR* InShaderSource,
	int32 SourceLen,
	GLSLVersion Version
	)
{
	const ANSICHAR* USFSource = InShaderSource;
	CrossCompiler::FHlslccHeader CCHeader;
	if (!CCHeader.Read(USFSource, SourceLen))
	{
		UE_LOG(LogOpenGLShaderCompiler, Error, TEXT("Bad hlslcc header found"));
	}
	
	if (*USFSource != '#')
	{
		UE_LOG(LogOpenGLShaderCompiler, Error, TEXT("Bad hlslcc header found! Missing '#'!"));
	}

	FOpenGLCodeHeader Header = {0};
	FShaderParameterMap& ParameterMap = ShaderOutput.ParameterMap;
	EShaderFrequency Frequency = (EShaderFrequency)ShaderOutput.Target.Frequency;

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

	// Write out the magic markers.
	Header.GlslMarker = 0x474c534c;
	switch (Frequency)
	{
	case SF_Vertex:
		Header.FrequencyMarker = 0x5653;
		break;
	case SF_Pixel:
		Header.FrequencyMarker = 0x5053;
		break;
	case SF_Geometry:
		Header.FrequencyMarker = 0x4753;
		break;
	case SF_Hull:
		Header.FrequencyMarker = 0x4853;
		break;
	case SF_Domain:
		Header.FrequencyMarker = 0x4453;
		break;
	case SF_Compute:
		Header.FrequencyMarker = 0x4353;
		break;
	default:
		UE_LOG(LogOpenGLShaderCompiler, Fatal, TEXT("Invalid shader frequency: %d"), (int32)Frequency);
	}

	static const FString AttributePrefix = TEXT("in_ATTRIBUTE");
	static const FString GL_Prefix = TEXT("gl_");
	for (auto& Input : CCHeader.Inputs)
	{
		// Only process attributes for vertex shaders.
		if (Frequency == SF_Vertex && Input.Name.StartsWith(AttributePrefix))
		{
			int32 AttributeIndex = ParseNumber(*Input.Name + AttributePrefix.Len());
			Header.Bindings.InOutMask |= (1 << AttributeIndex);
		}
		// Record user-defined input varyings
		else if (!Input.Name.StartsWith(GL_Prefix))
		{
			FOpenGLShaderVarying Var;
			Var.Location = Input.Index;
			Var.Varying = ParseIdentifierANSI(Input.Name);
			Header.Bindings.InputVaryings.Add(Var);
		}
	}

	// Generate vertex attribute remapping table.
	// This is used on devices where GL_MAX_VERTEX_ATTRIBS < 16
	if (Frequency == SF_Vertex)
	{
		uint32 AttributeMask = Header.Bindings.InOutMask;
		int32 NextAttributeSlot = 0;
		Header.Bindings.VertexRemappedMask = 0;
		for (int32 AttributeIndex = 0; AttributeIndex < 16; AttributeIndex++, AttributeMask >>= 1)
		{
			if (AttributeMask & 0x1)
			{
				Header.Bindings.VertexRemappedMask |= (1 << NextAttributeSlot);
				Header.Bindings.VertexAttributeRemap[AttributeIndex] = NextAttributeSlot++;
			}
			else
			{
				Header.Bindings.VertexAttributeRemap[AttributeIndex] = -1;
			}
		}
	}
コード例 #2
0
void BuildResourceTableMapping(
	const TMap<FString,FResourceTableEntry>& ResourceTableMap,
	const TMap<FString,uint32>& ResourceTableLayoutHashes,
	TBitArray<>& UsedUniformBufferSlots,
	FShaderParameterMap& ParameterMap,
	FShaderResourceTable& OutSRT)
{
	check(OutSRT.ResourceTableBits == 0);
	check(OutSRT.ResourceTableLayoutHashes.Num() == 0);

	// Build resource table mapping
	int32 MaxBoundResourceTable = -1;
	TArray<uint32> ResourceTableSRVs;
	TArray<uint32> ResourceTableSamplerStates;
	TArray<uint32> ResourceTableUAVs;

	for( auto MapIt = ResourceTableMap.CreateConstIterator(); MapIt; ++MapIt )
	{
		const FString& Name	= MapIt->Key;
		const FResourceTableEntry& Entry = MapIt->Value;

		uint16 BufferIndex, BaseIndex, Size;
		if (ParameterMap.FindParameterAllocation( *Name, BufferIndex, BaseIndex, Size ) )
		{
			ParameterMap.RemoveParameterAllocation(*Name);

			uint16 UniformBufferIndex = INDEX_NONE, UBBaseIndex, UBSize;
			if (ParameterMap.FindParameterAllocation(*Entry.UniformBufferName, UniformBufferIndex, UBBaseIndex, UBSize) == false)
			{
				UniformBufferIndex = UsedUniformBufferSlots.FindAndSetFirstZeroBit();
				ParameterMap.AddParameterAllocation(*Entry.UniformBufferName,UniformBufferIndex,0,0);
			}

			OutSRT.ResourceTableBits |= (1 << UniformBufferIndex);
			MaxBoundResourceTable = FMath::Max<int32>(MaxBoundResourceTable, (int32)UniformBufferIndex);

			while (OutSRT.ResourceTableLayoutHashes.Num() <= MaxBoundResourceTable)
			{
				OutSRT.ResourceTableLayoutHashes.Add(0);
			}
			OutSRT.ResourceTableLayoutHashes[UniformBufferIndex] = ResourceTableLayoutHashes.FindChecked(Entry.UniformBufferName);

			auto ResourceMap = FRHIResourceTableEntry::Create(UniformBufferIndex, Entry.ResourceIndex, BaseIndex);
			switch( Entry.Type )
			{
			case UBMT_TEXTURE:
				OutSRT.TextureMap.Add(ResourceMap);
				break;
			case UBMT_SAMPLER:
				OutSRT.SamplerMap.Add(ResourceMap);
				break;
			case UBMT_SRV:
				OutSRT.ShaderResourceViewMap.Add(ResourceMap);
				break;
			case UBMT_UAV:
				OutSRT.UnorderedAccessViewMap.Add(ResourceMap);
				break;
			default:
				check(0);
			}
		}
	}

	OutSRT.MaxBoundResourceTable = MaxBoundResourceTable;
}
コード例 #3
0
//---------------------------------------------------------------------------
// Configure the chunk map for this device.  The empty chunk map is an array
// of bool that maps which chunks consist entirely of unallocated clusters.
//
unsigned __fastcall TImageStore::LoadEmptyChunkMap(unsigned int ChunkSize)
{
    unsigned nChunkCount, nClustersPerChunk, nBitmapBytes;
    VOLUME_BITMAP_BUFFER *VolumeBitmap;
    TBitArray *Bitmap = new TBitArray;
    int nExtraClusterBits;
    bool bChunkEmpty;
    unsigned nEmptyChunkCount = 0;

    // IF this isn't a Linux or local Windows partition then we have no business here
    if (PartitionType == ptUnknown || PartitionType == ptWindows && FType != isDrive )
        return 0;

    // In cases where the input size is an exact multiple of the chunk size, we actually want nChunkCount to be one
    // higher than the actual number of chunks.  In that case, the last real chunk can potentially be empty, and the
    // next read will return zero bytes, telling the read thread that it's at EOF.
    nChunkCount = (Geometry.Bytes + ChunkSize) / ChunkSize;

    if (EmptyChunkMap)
        delete EmptyChunkMap;
    EmptyChunkMap = new TBitArray();
    EmptyChunkMap->Size = nChunkCount;
    nChunkSize = ChunkSize;

    // Determine the number of clusters in a chunk, and the number of cluster bitmap bytes that are taken by a chunk.
    nClustersPerChunk = (ChunkSize + Geometry.BytesPerCluster - 1) / Geometry.BytesPerCluster;
    nBitmapBytes = (nClustersPerChunk + 7) / 8;
    // Once we know all that, we can determine the size of the volume bitmap buffer that we need for each chunk
    // nBitmanBytes is actually one larger than we need because there is one bitmap byte already in the VOLUME_BITMAP_BUFFER
    // struct.  This is good, though, because FSCTL_GET_VOLUME_BITMAP rounds the start cluster down to a multiple of 8, so we
    // may need this extra space.
    VolumeBitmap = (VOLUME_BITMAP_BUFFER *)SysGetMem(sizeof(VOLUME_BITMAP_BUFFER) + nBitmapBytes);

    // Create the progress bar dialog
    Application->CreateForm(__classid(TProgressDialog), &ProgressDialog);
    ProgressDialog->Display("Mapping unallocated space...", 0, nChunkCount);

    // Iterate through all the chunks and determine if the chunk resides entirely on clusters that are unallocated
    for (unsigned n = 0; n < nChunkCount - 1; n++) {
        LONGLONG nnStartCluster = (LONGLONG)n * (LONGLONG)ChunkSize / (LONGLONG)Geometry.BytesPerCluster;
        DWORD nBytesReturned;

        // Ask for a cluster bitmap starting with the cluster this chunk starts on
        if (PartitionType == ptWindows) {
            int ret = DeviceIoControl(hHandle, FSCTL_GET_VOLUME_BITMAP, &nnStartCluster, sizeof(nnStartCluster), VolumeBitmap, sizeof(VOLUME_BITMAP_BUFFER) + nBitmapBytes, &nBytesReturned, NULL);
            if (!ret && GetLastError() != ERROR_MORE_DATA) {
                delete EmptyChunkMap;
                EmptyChunkMap = NULL;
                nEmptyChunkCount = 0;
                break;
            }  // if (!ret && GetLastError() != ERROR_MORE_DATA)
        }  // if (PartitionType == ptWindows)
        else if (PartitionType == ptEXT2) {
            bool ret;
            VolumeBitmap->StartingLcn.QuadPart = nnStartCluster;
            ret = GetEXT2VolumeBitmap(&VolumeBitmap->StartingLcn.QuadPart, VolumeBitmap->Buffer, nBitmapBytes + 1);
            if (!ret) {
                delete EmptyChunkMap;
                EmptyChunkMap = NULL;
                nEmptyChunkCount = 0;
                break;
            }  // if (!ret)
        }  // if (PartitionType == ptEXT2)

        nExtraClusterBits = nnStartCluster - VolumeBitmap->StartingLcn.QuadPart;
        VolumeBitmap->Buffer[0] &= 0xFF>>nExtraClusterBits;
        Bitmap->LoadBuffer(VolumeBitmap->Buffer, nClustersPerChunk + nExtraClusterBits);
        bChunkEmpty = !Bitmap->HasSetBit();
        if (bChunkEmpty)
            nEmptyChunkCount++;
        EmptyChunkMap->Bits[n] = bChunkEmpty;
        if (!(n & 7))
            ProgressDialog->Position = n;
    }  // for (unsigned n = 0; n < nnChunkCount; n++)
    ProgressDialog->Position = nChunkCount;

    SysFreeMem(VolumeBitmap);
    delete Bitmap;
    if (PartitionType == ptEXT2)
        GetEXT2VolumeBitmap(NULL, NULL, 0, true);

    Seek(0, FILE_BEGIN);

    ProgressDialog->Close();
    delete ProgressDialog;

    // The last chunk must always be read normally so that the resulting image is sized properly
    if (EmptyChunkMap)
        EmptyChunkMap->Bits[nChunkCount - 1] = false;

    return nEmptyChunkCount;
}  // void __fastcall TImageStore:LoadChunkMap(unsigned int ChunkSize)
コード例 #4
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
	}
}