// Copyright Epic Games, Inc. All Rights Reserved. #include "HlslccHeaderWriter.h" #include "ShaderConductorContext.h" THIRD_PARTY_INCLUDES_START #include "spirv_reflect.h" THIRD_PARTY_INCLUDES_END namespace CrossCompiler { template static void MetaDataPrintf(FString& MetaData, const FmtType& Fmt, Types... Args) { if (!MetaData.IsEmpty()) { MetaData += TEXT(","); } MetaData += FString::Printf(Fmt, Args...); } void FHlslccHeaderWriter::WriteInputAttribute(const SpvReflectInterfaceVariable& Attribute) { WriteIOAttribute(Strings.InputAttributes, Attribute, /*bIsInput:*/ true); } void FHlslccHeaderWriter::WriteInputAttribute(const TCHAR* AttributeName, const TCHAR* TypeSpecifier, int32 Location, bool bLocationPrefix, bool bLocationSuffix) { WriteIOAttribute(Strings.InputAttributes, AttributeName, TypeSpecifier, Location, bLocationPrefix, bLocationSuffix); } void FHlslccHeaderWriter::WriteOutputAttribute(const SpvReflectInterfaceVariable& Attribute) { WriteIOAttribute(Strings.OutputAttributes, Attribute, /*bIsInput:*/ false); } void FHlslccHeaderWriter::WriteOutputAttribute(const TCHAR* AttributeName, const TCHAR* TypeSpecifier, int32 Location, bool bLocationPrefix, bool bLocationSuffix) { WriteIOAttribute(Strings.OutputAttributes, AttributeName, TypeSpecifier, Location, bLocationPrefix, bLocationSuffix); } static void ConvertMetaDataTypeSpecifierPrimary(const SpvReflectTypeDescription& TypeSpecifier, FString& OutTypeName, uint32& OutTypeBitWidth, bool bBaseTypeOnly) { // Generate prefix for base type if (TypeSpecifier.type_flags & SPV_REFLECT_TYPE_FLAG_BOOL) { OutTypeName += TEXT('b'); OutTypeBitWidth = 8; } else if (TypeSpecifier.type_flags & SPV_REFLECT_TYPE_FLAG_INT) { if (TypeSpecifier.traits.numeric.scalar.signedness) { OutTypeName += TEXT('i'); } else { OutTypeName += TEXT('u'); } OutTypeBitWidth = 32; } else if (TypeSpecifier.type_flags & SPV_REFLECT_TYPE_FLAG_FLOAT) { if (TypeSpecifier.traits.numeric.scalar.width == 16) { OutTypeName += TEXT('h'); OutTypeBitWidth = 16; } else { OutTypeName += TEXT('f'); OutTypeBitWidth = 32; } } if (!bBaseTypeOnly) { // Generate number for vector size const SpvReflectTypeFlags SpvScalarTypeFlags = (SPV_REFLECT_TYPE_FLAG_BOOL | SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_FLOAT); if (TypeSpecifier.type_flags & SPV_REFLECT_TYPE_FLAG_VECTOR) { static const TCHAR* VectorDims = TEXT("1234"); const uint32 VectorSize = TypeSpecifier.traits.numeric.vector.component_count; check(VectorSize >= 1 && VectorSize <= 4); OutTypeName += VectorDims[VectorSize - 1]; } else if (TypeSpecifier.type_flags & SPV_REFLECT_TYPE_FLAG_MATRIX) { //TODO } else if ((TypeSpecifier.type_flags & SpvScalarTypeFlags) != 0) { OutTypeName += TEXT('1'); // add single scalar component } } } static FString ConvertMetaDataTypeSpecifier(const SpvReflectTypeDescription& TypeSpecifier, uint32* OutTypeBitWidth = nullptr, bool bBaseTypeOnly = false) { FString TypeName; uint32 TypeBitWidth = sizeof(float) * 8; ConvertMetaDataTypeSpecifierPrimary(TypeSpecifier, TypeName, TypeBitWidth, bBaseTypeOnly); if (OutTypeBitWidth) { *OutTypeBitWidth = TypeBitWidth; } return TypeName; } static const TCHAR* SpvBuiltinToString(const SpvBuiltIn BuiltIn) { switch (BuiltIn) { case SpvBuiltInPosition: return TEXT("gl_Position"); case SpvBuiltInPointSize: return TEXT("gl_PointSize"); case SpvBuiltInClipDistance: return TEXT("gl_ClipDistance"); case SpvBuiltInCullDistance: return TEXT("gl_CullDistance"); case SpvBuiltInVertexId: return TEXT("gl_VertexID"); case SpvBuiltInInstanceId: return TEXT("gl_InstanceID"); case SpvBuiltInPrimitiveId: return TEXT("gl_PrimitiveID"); case SpvBuiltInInvocationId: return TEXT("gl_InvocationID"); case SpvBuiltInLayer: return TEXT("gl_Layer"); case SpvBuiltInViewportIndex: return TEXT("gl_ViewportIndex"); case SpvBuiltInTessLevelOuter: return TEXT("gl_TessLevelOuter"); case SpvBuiltInTessLevelInner: return TEXT("gl_TessLevelInner"); case SpvBuiltInTessCoord: return TEXT("gl_TessCoord"); case SpvBuiltInPatchVertices: return TEXT("gl_PatchVertices"); case SpvBuiltInFragCoord: return TEXT("gl_FragCoord"); case SpvBuiltInPointCoord: return TEXT("gl_PointCoord"); case SpvBuiltInFrontFacing: return TEXT("gl_FrontFacing"); case SpvBuiltInSampleId: return TEXT("gl_SampleID"); case SpvBuiltInSamplePosition: return TEXT("gl_SamplePosition"); case SpvBuiltInSampleMask: return TEXT("gl_SampleMask"); case SpvBuiltInFragDepth: return TEXT("gl_FragDepth"); case SpvBuiltInHelperInvocation: return TEXT("gl_HelperInvocation"); case SpvBuiltInNumWorkgroups: return TEXT("gl_NumWorkgroups"); case SpvBuiltInWorkgroupSize: return TEXT("gl_WorkgroupSize"); case SpvBuiltInWorkgroupId: return TEXT("gl_WorkgroupID"); case SpvBuiltInLocalInvocationId: return TEXT("gl_LocalInvocationID"); case SpvBuiltInGlobalInvocationId: return TEXT("gl_GlobalInvocationID"); case SpvBuiltInLocalInvocationIndex: return TEXT("gl_LocalInvocationIndex"); case SpvBuiltInWorkDim: return TEXT("gl_WorkDim"); case SpvBuiltInGlobalSize: return TEXT("gl_GlobalSize"); case SpvBuiltInEnqueuedWorkgroupSize: return TEXT("gl_EnqueuedWorkgroupSize"); case SpvBuiltInGlobalOffset: return TEXT("gl_GlobalOffset"); case SpvBuiltInGlobalLinearId: return TEXT("gl_GlobalLinearID"); case SpvBuiltInSubgroupSize: return TEXT("gl_SubgroupSize"); case SpvBuiltInSubgroupMaxSize: return TEXT("gl_SubgroupMaxSize"); case SpvBuiltInNumSubgroups: return TEXT("gl_NumSubgroups"); case SpvBuiltInNumEnqueuedSubgroups: return TEXT("gl_NumEnqueuedSubgroups"); case SpvBuiltInSubgroupId: return TEXT("gl_SubgroupID"); case SpvBuiltInSubgroupLocalInvocationId: return TEXT("gl_SubgroupLocalInvocationID"); case SpvBuiltInVertexIndex: return TEXT("gl_VertexIndex"); case SpvBuiltInInstanceIndex: return TEXT("gl_InstanceIndex"); case SpvBuiltInSubgroupEqMask: return TEXT("gl_SubgroupEqMask"); case SpvBuiltInSubgroupGeMask: return TEXT("gl_SubgroupGeMask"); case SpvBuiltInSubgroupGtMask: return TEXT("gl_SubgroupGtMask"); case SpvBuiltInSubgroupLeMask: return TEXT("gl_SubgroupLeMask"); case SpvBuiltInSubgroupLtMask: return TEXT("gl_SubgroupLtMask"); case SpvBuiltInBaseVertex: return TEXT("gl_BaseVertex"); case SpvBuiltInBaseInstance: return TEXT("gl_BaseInstance"); case SpvBuiltInDrawIndex: return TEXT("gl_DrawIndex"); case SpvBuiltInDeviceIndex: return TEXT("gl_DeviceIndex"); case SpvBuiltInViewIndex: return TEXT("gl_ViewIndex"); } return nullptr; } static FString ConvertAttributeToMetaDataSemantic(const ANSICHAR* AttributeName, const SpvBuiltIn BuiltIn, bool bIsInput) { if (const TCHAR* BuiltInName = SpvBuiltinToString(BuiltIn)) { return FString(BuiltInName); } else { check(AttributeName != nullptr && *AttributeName != '\0'); FString InSemantic = ANSI_TO_TCHAR(AttributeName); FString OutSemantic = (bIsInput ? TEXT("in_") : TEXT("out_")); if (InSemantic.StartsWith(TEXT("SV_"))) { OutSemantic += InSemantic.Right(InSemantic.Len() - 3); } else { OutSemantic += InSemantic; } return OutSemantic; } } // Flattens the array dimensions of the interface variable (aka shader attribute), e.g. from float4[2][3] -> float4[6] static uint32 FlattenAttributeArrayDimension(const SpvReflectInterfaceVariable& Attribute, uint32 FirstArrayDim = 0) { uint32 FlattenedArrayDim = 1; for (uint32 ArrayDimIndex = FirstArrayDim; ArrayDimIndex < Attribute.array.dims_count; ++ArrayDimIndex) { FlattenedArrayDim *= Attribute.array.dims[ArrayDimIndex]; } return FlattenedArrayDim; } // Returns the string position where the index in the specified HLSL semantic beings, e.g. "SV_Target2" -> 9, "SV_Target" -> INDEX_NONE static int32 FindIndexInHlslSemantic(const FString& Semantic) { int32 Index = Semantic.Len(); if (Index > 0 && FChar::IsDigit(Semantic[Index - 1])) { while (Index > 0 && FChar::IsDigit(Semantic[Index - 1])) { --Index; } return Index; } return INDEX_NONE; } // private void FHlslccHeaderWriter::WriteIOAttribute(FString& OutMetaData, const TCHAR* AttributeName, const TCHAR* TypeSpecifier, int32 Location, bool bLocationPrefix, bool bLocationSuffix) { MetaDataPrintf(OutMetaData, TEXT("%s"), TypeSpecifier); if (bLocationPrefix) { OutMetaData += FString::Printf(TEXT(";%d:"), Location); } else { OutMetaData += TEXT(":"); } if (bLocationSuffix) { OutMetaData += FString::Printf(TEXT("%s%d"), AttributeName, Location); } else { OutMetaData += AttributeName; } } // private void FHlslccHeaderWriter::WriteIOAttribute(FString& OutMetaData, const SpvReflectInterfaceVariable& Attribute, bool bIsInput) { // Ignore interface variables that are only generated for intermediate results if (CrossCompiler::FShaderConductorContext::IsIntermediateSpirvOutputVariable(Attribute.name)) { return; } const FString TypeSpecifier = ConvertMetaDataTypeSpecifier(*Attribute.type_description); FString Semantic = ConvertAttributeToMetaDataSemantic(Attribute.semantic, Attribute.built_in, bIsInput); if (Attribute.array.dims_count > 0) { // Get semantic without index, e.g. "out_Target0" -> "out_Target" const int32 SemanticIndexPos = FindIndexInHlslSemantic(Semantic); if (SemanticIndexPos != INDEX_NONE) { Semantic = Semantic.Left(SemanticIndexPos); } if (Attribute.location == -1) { // Flatten array dimensions, e.g. from float4[3][2] -> float4[6] const uint32 FlattenedArrayDim = FlattenAttributeArrayDimension(Attribute); // Emit one output slot for each array element, e.g. "out float4 OutColor[2] : SV_Target0" occupies output slot SV_Target0 and SV_Target1. for (uint32 FlattenedArrayIndex = 0; FlattenedArrayIndex < FlattenedArrayDim; ++FlattenedArrayIndex) { // If there is no binding slot, emit output as system value array such as "gl_SampleMask[]" MetaDataPrintf( OutMetaData, TEXT("%s;%d:%s[%d]"), *TypeSpecifier, // type specifier Attribute.location, *Semantic, FlattenedArrayIndex ); } } else if (!bIsInput) { //NOTE: For some reason, the meta data for output slot arrays must be entirely flattened, including the outer most array dimension // Flatten array dimensions, e.g. from float4[3][2] -> float4[6] const uint32 FlattenedArrayDim = FlattenAttributeArrayDimension(Attribute); // Emit one output slot for each array element, e.g. "out float4 OutColor[2] : SV_Target0" occupies output slot SV_Target0 and SV_Target1. for (uint32 FlattenedArrayIndex = 0; FlattenedArrayIndex < FlattenedArrayDim; ++FlattenedArrayIndex) { const uint32 BindingSlot = Attribute.location + FlattenedArrayIndex; MetaDataPrintf( OutMetaData, TEXT("%s;%d:%s%d"), *TypeSpecifier, // Type specifier BindingSlot, *Semantic, BindingSlot ); } } else if (Attribute.array.dims_count >= 2) { // Flatten array dimensions, e.g. from float4[3][2] -> float4[6] const uint32 FlattenedArrayDim = FlattenAttributeArrayDimension(Attribute, 1); // Emit one output slot for each array element, e.g. "out float4 OutColor[2] : SV_Target0" occupies output slot SV_Target0 and SV_Target1. for (uint32 FlattenedArrayIndex = 0; FlattenedArrayIndex < FlattenedArrayDim; ++FlattenedArrayIndex) { const uint32 BindingSlot = Attribute.location + FlattenedArrayIndex; MetaDataPrintf( OutMetaData, TEXT("%s[%d];%d:%s%d"), *TypeSpecifier, // Type specifier Attribute.array.dims[0], // Outer most array dimension BindingSlot, *Semantic, BindingSlot ); } } else { const uint32 BindingSlot = Attribute.location; MetaDataPrintf( OutMetaData, TEXT("%s[%d];%d:%s%d"), *TypeSpecifier, // Type specifier Attribute.array.dims[0], // Outer most array dimension BindingSlot, *Semantic, BindingSlot ); } } else { MetaDataPrintf( OutMetaData, TEXT("%s;%d:%s"), *TypeSpecifier, // type specifier Attribute.location, *Semantic ); } } void FHlslccHeaderWriter::WriteUniformBlock(const TCHAR* ResourceName, uint32 BindingIndex) { MetaDataPrintf(Strings.UniformBlocks, TEXT("%s(%u)"), ResourceName, BindingIndex); } void FHlslccHeaderWriter::WritePackedGlobal(const TCHAR* ResourceName, const TCHAR* TypeSpecifier, uint32 ByteOffset, uint32 ByteSize) { checkf(ByteOffset % 4 == 0, TEXT("field offset in @PackedGlobals shader meta data must be a multiple of 4, but got %u"), ByteOffset); checkf(ByteSize % 4 == 0, TEXT("field size in @PackedGlobals shader meta data must be a multiple of 4, but got %u"), ByteSize); MetaDataPrintf(Strings.PackedGlobals, TEXT("%s(%s:%u,%u)"), ResourceName, TypeSpecifier, ByteOffset / 4, ByteSize / 4); } void FHlslccHeaderWriter::WritePackedUB(uint32 BindingIndex) { checkf(Strings.PackedUB.IsEmpty(), TEXT("cannot define @PackedUB attribute more than once")); MetaDataPrintf(Strings.PackedUB, TEXT("Globals(%u): "), BindingIndex); } void FHlslccHeaderWriter::WritePackedUBField(const TCHAR* ResourceName, uint32 ByteOffset, uint32 ByteSize) { checkf(!Strings.PackedUB.IsEmpty(), TEXT("cannot append field without @PackedUB attribute in shader meta data")); checkf(ByteOffset % 4 == 0, TEXT("field offset in @PackedUB shader meta data must be a multiple of 4, but got %u"), ByteOffset); checkf(ByteSize % 4 == 0, TEXT("field size in @PackedUB shader meta data must be a multiple of 4, but got %u"), ByteSize); MetaDataPrintf(Strings.PackedUBFields, TEXT("%s(%u,%u)"), ResourceName, ByteOffset / 4, ByteSize / 4); } void FHlslccHeaderWriter::WritePackedUBGlobalCopy(uint32 SourceCB, uint32 SourceOffset, uint32 DestCBIndex, uint32 DestCBPrecision, uint32 DestOffset, uint32 Size, bool bGroupFlattenedUBs) { if (bGroupFlattenedUBs) { MetaDataPrintf(Strings.PackedUBGlobalCopies, TEXT("%u:%u-%u:%c:%u:%u"), SourceCB, SourceOffset, DestCBIndex, DestCBPrecision, DestOffset, Size); } else { check(DestCBIndex == 0); MetaDataPrintf(Strings.PackedUBGlobalCopies, TEXT("%u:%u-%c:%u:%u"), SourceCB, SourceOffset, DestCBPrecision, DestOffset, Size); } } void FHlslccHeaderWriter::WriteSRV(const TCHAR* ResourceName, uint32 BindingIndex, uint32 Count) { MetaDataPrintf(Strings.SRVs, TEXT("%s(%u:%u)"), ResourceName, BindingIndex, Count); } void FHlslccHeaderWriter::WriteSRV(const TCHAR* ResourceName, uint32 BindingIndex, uint32 Count, const TArray& AssociatedResourceNames) { MetaDataPrintf(Strings.SRVs, TEXT("%s(%u:%u"), ResourceName, BindingIndex, Count); if (!AssociatedResourceNames.IsEmpty()) { Strings.SRVs += TEXT("["); for (int32 ArrayIndex = 0; ArrayIndex < AssociatedResourceNames.Num(); ++ArrayIndex) { if (ArrayIndex > 0) { Strings.SRVs += TEXT(","); } Strings.SRVs += AssociatedResourceNames[ArrayIndex]; } Strings.SRVs += TEXT("]"); } Strings.SRVs += TEXT(")"); } void FHlslccHeaderWriter::WriteUAV(const TCHAR* ResourceName, uint32 BindingIndex, uint32 Count) { MetaDataPrintf(Strings.UAVs, TEXT("%s(%u:%u)"), ResourceName, BindingIndex, Count); } void FHlslccHeaderWriter::WriteSamplerState(const TCHAR* ResourceName, uint32 BindingIndex) { MetaDataPrintf(Strings.SamplerStates, TEXT("%u:%s"), BindingIndex, ResourceName); } void FHlslccHeaderWriter::WriteNumThreads(uint32 NumThreadsX, uint32 NumThreadsY, uint32 NumThreadsZ) { MetaDataPrintf(Strings.NumThreads, TEXT("%u, %u, %u"), NumThreadsX, NumThreadsY, NumThreadsZ); } void FHlslccHeaderWriter::WriteSideTable(const TCHAR* ResourceName, uint32 SideTableIndex) { MetaDataPrintf(Strings.SideTable, TEXT("%s(%d)"), ResourceName, SideTableIndex); } void FHlslccHeaderWriter::WriteArgumentBuffers(uint32 BindingIndex, const TArray& ResourceIndices) { MetaDataPrintf(Strings.ArgumentBuffers, TEXT("%d["), BindingIndex); for (int32 ArrayIndex = 0; ArrayIndex < ResourceIndices.Num(); ++ArrayIndex) { if (ArrayIndex > 0) { Strings.ArgumentBuffers += TEXT(","); } Strings.ArgumentBuffers += FString::Printf(TEXT("%u"), ResourceIndices[ArrayIndex]); } Strings.ArgumentBuffers += TEXT("]"); } void FHlslccHeaderWriter::WriteTessellationInputControlPoints(uint32 PatchSize) { checkf(Strings.TessellationInputControlPoints.IsEmpty(), TEXT("cannot define @TessellationInputControlPoints attribute more than once")); Strings.TessellationInputControlPoints = FString::Printf(TEXT("%u"), PatchSize); } void FHlslccHeaderWriter::WriteTessellationOutputControlPoints(uint32 PatchSize) { checkf(Strings.TessellationOutputControlPoints.IsEmpty(), TEXT("cannot define @TessellationOutputControlPoints attribute more than once")); Strings.TessellationOutputControlPoints = FString::Printf(TEXT("%u"), PatchSize); } void FHlslccHeaderWriter::WriteTessellationMaxTessFactor(uint32 MaxTessFactor) { checkf(Strings.TessellationMaxTessFactor.IsEmpty(), TEXT("cannot define @TessellationMaxTessFactor attribute more than once")); Strings.TessellationMaxTessFactor = FString::Printf(TEXT("%u"), MaxTessFactor); } void FHlslccHeaderWriter::WriteTessellationDomainTri() { checkf(Strings.TessellationDomain.IsEmpty(), TEXT("cannot define @TessellationDomain attribute more than once")); Strings.TessellationDomain = TEXT("tri"); } void FHlslccHeaderWriter::WriteTessellationDomainQuad() { checkf(Strings.TessellationDomain.IsEmpty(), TEXT("cannot define @TessellationDomain attribute more than once")); Strings.TessellationDomain = TEXT("quad"); } void FHlslccHeaderWriter::WriteTessellationOutputWindingCW() { checkf(Strings.TessellationOutputWinding.IsEmpty(), TEXT("cannot define @TessellationOutputWinding attribute more than once")); Strings.TessellationOutputWinding = TEXT("cw"); } void FHlslccHeaderWriter::WriteTessellationOutputWindingCCW() { checkf(Strings.TessellationOutputWinding.IsEmpty(), TEXT("cannot define @TessellationOutputWinding attribute more than once")); Strings.TessellationOutputWinding = TEXT("ccw"); } void FHlslccHeaderWriter::WriteTessellationPartitioningInteger() { checkf(Strings.TessellationPartitioning.IsEmpty(), TEXT("cannot define @TessellationPartitioning attribute more than once")); Strings.TessellationPartitioning = TEXT("integer"); } void FHlslccHeaderWriter::WriteTessellationPartitioningFractionalOdd() { checkf(Strings.TessellationPartitioning.IsEmpty(), TEXT("cannot define @TessellationPartitioning attribute more than once")); Strings.TessellationPartitioning = TEXT("fractional_odd"); } void FHlslccHeaderWriter::WriteTessellationPartitioningFractionalEven() { checkf(Strings.TessellationPartitioning.IsEmpty(), TEXT("cannot define @TessellationPartitioning attribute more than once")); Strings.TessellationPartitioning = TEXT("fractional_even"); } void FHlslccHeaderWriter::WriteTessellationPatchesPerThreadGroup(uint32 NumPatches) { checkf(Strings.TessellationPatchesPerThreadGroup.IsEmpty(), TEXT("cannot define @TessellationPatchesPerThreadGroup attribute more than once")); Strings.TessellationPatchesPerThreadGroup = FString::Printf(TEXT("%u"), NumPatches); } void FHlslccHeaderWriter::WriteTessellationPatchCountBuffer(uint32 BindingIndex) { checkf(Strings.TessellationPatchCountBuffer.IsEmpty(), TEXT("cannot define @TessellationPatchCountBuffer attribute more than once")); Strings.TessellationPatchCountBuffer = FString::Printf(TEXT("%u"), BindingIndex); } void FHlslccHeaderWriter::WriteTessellationIndexBuffer(uint32 BindingIndex) { checkf(Strings.TessellationIndexBuffer.IsEmpty(), TEXT("cannot define @TessellationIndexBuffer attribute more than once")); Strings.TessellationIndexBuffer = FString::Printf(TEXT("%u"), BindingIndex); } void FHlslccHeaderWriter::WriteTessellationHSOutBuffer(uint32 BindingIndex) { checkf(Strings.TessellationHSOutBuffer.IsEmpty(), TEXT("cannot define @TessellationHSOutBuffer attribute more than once")); Strings.TessellationHSOutBuffer = FString::Printf(TEXT("%u"), BindingIndex); } void FHlslccHeaderWriter::WriteTessellationHSTFOutBuffer(uint32 BindingIndex) { checkf(Strings.TessellationHSTFOutBuffer.IsEmpty(), TEXT("cannot define @TessellationHSTFOutBuffer attribute more than once")); Strings.TessellationHSTFOutBuffer = FString::Printf(TEXT("%u"), BindingIndex); } void FHlslccHeaderWriter::WriteTessellationControlPointOutBuffer(uint32 BindingIndex) { checkf(Strings.TessellationControlPointOutBuffer.IsEmpty(), TEXT("cannot define @TessellationControlPointOutBuffer attribute more than once")); Strings.TessellationControlPointOutBuffer = FString::Printf(TEXT("%u"), BindingIndex); } void FHlslccHeaderWriter::WriteTessellationControlPointIndexBuffer(uint32 BindingIndex) { checkf(Strings.TessellationControlPointIndexBuffer.IsEmpty(), TEXT("cannot define @TessellationControlPointIndexBuffer attribute more than once")); Strings.TessellationControlPointIndexBuffer = FString::Printf(TEXT("%u"), BindingIndex); } FString FHlslccHeaderWriter::ToString() const { FString MetaData; auto PrintAttributes = [&MetaData](const TCHAR* Name, const FString& Value) { if (!Value.IsEmpty()) { MetaData += FString::Printf(TEXT("// @%s: %s\n"), Name, *Value); } }; PrintAttributes(TEXT("Inputs"), Strings.InputAttributes); PrintAttributes(TEXT("Outputs"), Strings.OutputAttributes); PrintAttributes(TEXT("UniformBlocks"), Strings.UniformBlocks); PrintAttributes(TEXT("PackedUB"), Strings.PackedUB + Strings.PackedUBFields); PrintAttributes(TEXT("PackedGlobals"), Strings.PackedGlobals); PrintAttributes(TEXT("PackedUBGlobalCopies"), Strings.PackedUBGlobalCopies); PrintAttributes(TEXT("Samplers"), Strings.SRVs); // Was called "Samplers" in HLSLcc but serves as SRVs PrintAttributes(TEXT("UAVs"), Strings.UAVs); PrintAttributes(TEXT("SamplerStates"), Strings.SamplerStates); PrintAttributes(TEXT("NumThreads"), Strings.NumThreads); PrintAttributes(TEXT("SideTable"), Strings.SideTable); PrintAttributes(TEXT("ArgumentBuffers"), Strings.ArgumentBuffers); PrintAttributes(TEXT("TessellationOutputControlPoints"), Strings.TessellationOutputControlPoints); PrintAttributes(TEXT("TessellationDomain"), Strings.TessellationDomain); PrintAttributes(TEXT("TessellationInputControlPoints"), Strings.TessellationInputControlPoints); PrintAttributes(TEXT("TessellationMaxTessFactor"), Strings.TessellationMaxTessFactor); PrintAttributes(TEXT("TessellationOutputWinding"), Strings.TessellationOutputWinding); PrintAttributes(TEXT("TessellationPartitioning"), Strings.TessellationPartitioning); PrintAttributes(TEXT("TessellationPatchesPerThreadGroup"), Strings.TessellationPatchesPerThreadGroup); PrintAttributes(TEXT("TessellationPatchCountBuffer"), Strings.TessellationPatchCountBuffer); PrintAttributes(TEXT("TessellationIndexBuffer"), Strings.TessellationIndexBuffer); PrintAttributes(TEXT("TessellationHSOutBuffer"), Strings.TessellationHSOutBuffer); PrintAttributes(TEXT("TessellationControlPointOutBuffer"), Strings.TessellationControlPointOutBuffer); PrintAttributes(TEXT("TessellationHSTFOutBuffer"), Strings.TessellationHSTFOutBuffer); PrintAttributes(TEXT("TessellationControlPointIndexBuffer"), Strings.TessellationControlPointIndexBuffer); return MetaData; } } // namespace CrossCompiler