diff --git a/src/libraries/spirv_cross/include/spirv_cross/sampler.hpp b/src/libraries/spirv_cross/include/spirv_cross/sampler.hpp index a95d489e2..542e11b44 100644 --- a/src/libraries/spirv_cross/include/spirv_cross/sampler.hpp +++ b/src/libraries/spirv_cross/include/spirv_cross/sampler.hpp @@ -85,7 +85,7 @@ struct sampler2DBase : spirv_cross_sampler_2d std::vector mips; spirv_cross_format format; spirv_cross_wrap wrap_s; - spirv_cross_format wrap_t; + spirv_cross_wrap wrap_t; spirv_cross_filter min_filter; spirv_cross_filter mag_filter; spirv_cross_mipfilter mip_filter; diff --git a/src/libraries/spirv_cross/spirv.h b/src/libraries/spirv_cross/spirv.h index 1b67617bf..92b34c387 100644 --- a/src/libraries/spirv_cross/spirv.h +++ b/src/libraries/spirv_cross/spirv.h @@ -1,5 +1,5 @@ /* -** Copyright (c) 2014-2019 The Khronos Group Inc. +** Copyright (c) 2014-2020 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and/or associated documentation files (the "Materials"), @@ -57,7 +57,7 @@ typedef unsigned int SpvId; #define SPV_REVISION 1 static const unsigned int SpvMagicNumber = 0x07230203; -static const unsigned int SpvVersion = 0x00010400; +static const unsigned int SpvVersion = 0x00010500; static const unsigned int SpvRevision = 1; static const unsigned int SpvOpCodeMask = 0xffff; static const unsigned int SpvWordCountShift = 16; @@ -82,11 +82,17 @@ typedef enum SpvExecutionModel_ { SpvExecutionModelKernel = 6, SpvExecutionModelTaskNV = 5267, SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, SpvExecutionModelCallableNV = 5318, SpvExecutionModelMax = 0x7fffffff, } SpvExecutionModel; @@ -183,11 +189,17 @@ typedef enum SpvStorageClass_ { SpvStorageClassAtomicCounter = 10, SpvStorageClassImage = 11, SpvStorageClassStorageBuffer = 12, + SpvStorageClassCallableDataKHR = 5328, SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, SpvStorageClassShaderRecordBufferNV = 5343, SpvStorageClassPhysicalStorageBuffer = 5349, SpvStorageClassPhysicalStorageBufferEXT = 5349, @@ -562,20 +574,35 @@ typedef enum SpvBuiltIn_ { SpvBuiltInFragmentSizeNV = 5292, SpvBuiltInFragInvocationCountEXT = 5293, SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInLaunchIdKHR = 5319, SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTKHR = 5332, SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, SpvBuiltInHitKindNV = 5333, + SpvBuiltInIncomingRayFlagsKHR = 5351, SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, SpvBuiltInWarpsPerSMNV = 5374, SpvBuiltInSMCountNV = 5375, SpvBuiltInWarpIDNV = 5376, @@ -713,6 +740,7 @@ typedef enum SpvScope_ { SpvScopeInvocation = 4, SpvScopeQueueFamily = 5, SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, SpvScopeMax = 0x7fffffff, } SpvScope; @@ -837,6 +865,8 @@ typedef enum SpvCapability_ { SpvCapabilitySignedZeroInfNanPreserve = 4466, SpvCapabilityRoundingModeRTE = 4467, SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, SpvCapabilityFloat16ImageAMD = 5008, SpvCapabilityImageGatherBiasLodAMD = 5009, SpvCapabilityFragmentMaskAMD = 5010, @@ -890,6 +920,7 @@ typedef enum SpvCapability_ { SpvCapabilityPhysicalStorageBufferAddresses = 5347, SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, SpvCapabilityCooperativeMatrixNV = 5357, SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, @@ -907,6 +938,53 @@ typedef enum SpvCapability_ { SpvCapabilityMax = 0x7fffffff, } SpvCapability; +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + typedef enum SpvOp_ { SpvOpNop = 0, SpvOpUndef = 1, @@ -1258,6 +1336,13 @@ typedef enum SpvOp_ { SpvOpSubgroupAnyKHR = 4429, SpvOpSubgroupAllEqualKHR = 4430, SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpTypeRayQueryProvisionalKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, SpvOpGroupIAddNonUniformAMD = 5000, SpvOpGroupFAddNonUniformAMD = 5001, SpvOpGroupFMinNonUniformAMD = 5002, @@ -1272,11 +1357,17 @@ typedef enum SpvOp_ { SpvOpImageSampleFootprintNV = 5283, SpvOpGroupNonUniformPartitionNV = 5296, SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpReportIntersectionKHR = 5334, SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionKHR = 5335, SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayKHR = 5336, SpvOpTerminateRayNV = 5336, SpvOpTraceNV = 5337, + SpvOpTraceRayKHR = 5337, + SpvOpTypeAccelerationStructureKHR = 5341, SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableKHR = 5344, SpvOpExecuteCallableNV = 5344, SpvOpTypeCooperativeMatrixNV = 5358, SpvOpCooperativeMatrixLoadNV = 5359, @@ -1433,6 +1524,23 @@ typedef enum SpvOp_ { SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, SpvOpMax = 0x7fffffff, } SpvOp; @@ -1810,6 +1918,30 @@ inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultTy case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; diff --git a/src/libraries/spirv_cross/spirv.hpp b/src/libraries/spirv_cross/spirv.hpp index f5cbda1bb..9f98683d4 100644 --- a/src/libraries/spirv_cross/spirv.hpp +++ b/src/libraries/spirv_cross/spirv.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 The Khronos Group Inc. +// Copyright (c) 2014-2020 The Khronos Group Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and/or associated documentation files (the "Materials"), @@ -53,7 +53,7 @@ typedef unsigned int Id; #define SPV_REVISION 1 static const unsigned int MagicNumber = 0x07230203; -static const unsigned int Version = 0x00010400; +static const unsigned int Version = 0x00010500; static const unsigned int Revision = 1; static const unsigned int OpCodeMask = 0xffff; static const unsigned int WordCountShift = 16; @@ -78,11 +78,17 @@ enum ExecutionModel { ExecutionModelKernel = 6, ExecutionModelTaskNV = 5267, ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, ExecutionModelCallableNV = 5318, ExecutionModelMax = 0x7fffffff, }; @@ -179,11 +185,17 @@ enum StorageClass { StorageClassAtomicCounter = 10, StorageClassImage = 11, StorageClassStorageBuffer = 12, + StorageClassCallableDataKHR = 5328, StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, StorageClassShaderRecordBufferNV = 5343, StorageClassPhysicalStorageBuffer = 5349, StorageClassPhysicalStorageBufferEXT = 5349, @@ -558,20 +570,35 @@ enum BuiltIn { BuiltInFragmentSizeNV = 5292, BuiltInFragInvocationCountEXT = 5293, BuiltInInvocationsPerPixelNV = 5293, + BuiltInLaunchIdKHR = 5319, BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, BuiltInWorldToObjectNV = 5331, + BuiltInHitTKHR = 5332, BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, BuiltInHitKindNV = 5333, + BuiltInIncomingRayFlagsKHR = 5351, BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, BuiltInWarpsPerSMNV = 5374, BuiltInSMCountNV = 5375, BuiltInWarpIDNV = 5376, @@ -709,6 +736,7 @@ enum Scope { ScopeInvocation = 4, ScopeQueueFamily = 5, ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, ScopeMax = 0x7fffffff, }; @@ -833,6 +861,8 @@ enum Capability { CapabilitySignedZeroInfNanPreserve = 4466, CapabilityRoundingModeRTE = 4467, CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, CapabilityFloat16ImageAMD = 5008, CapabilityImageGatherBiasLodAMD = 5009, CapabilityFragmentMaskAMD = 5010, @@ -886,6 +916,7 @@ enum Capability { CapabilityPhysicalStorageBufferAddresses = 5347, CapabilityPhysicalStorageBufferAddressesEXT = 5347, CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, CapabilityCooperativeMatrixNV = 5357, CapabilityFragmentShaderSampleInterlockEXT = 5363, CapabilityFragmentShaderShadingRateInterlockEXT = 5372, @@ -903,6 +934,53 @@ enum Capability { CapabilityMax = 0x7fffffff, }; +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + enum Op { OpNop = 0, OpUndef = 1, @@ -1254,6 +1332,13 @@ enum Op { OpSubgroupAnyKHR = 4429, OpSubgroupAllEqualKHR = 4430, OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, OpGroupIAddNonUniformAMD = 5000, OpGroupFAddNonUniformAMD = 5001, OpGroupFMinNonUniformAMD = 5002, @@ -1268,11 +1353,17 @@ enum Op { OpImageSampleFootprintNV = 5283, OpGroupNonUniformPartitionNV = 5296, OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, OpTerminateRayNV = 5336, OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, OpExecuteCallableNV = 5344, OpTypeCooperativeMatrixNV = 5358, OpCooperativeMatrixLoadNV = 5359, @@ -1429,6 +1520,23 @@ enum Op { OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, OpMax = 0x7fffffff, }; @@ -1806,6 +1914,30 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; case OpTraceNV: *hasResult = false; *hasResultType = false; break; case OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case OpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; @@ -1974,6 +2106,7 @@ inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } } // end namespace spv diff --git a/src/libraries/spirv_cross/spirv_cfg.cpp b/src/libraries/spirv_cross/spirv_cfg.cpp index 463c7562e..9973fbae1 100644 --- a/src/libraries/spirv_cross/spirv_cfg.cpp +++ b/src/libraries/spirv_cross/spirv_cfg.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 Arm Limited + * Copyright 2016-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_cfg.hpp b/src/libraries/spirv_cross/spirv_cfg.hpp index 5f62cca30..9f3e62a1b 100644 --- a/src/libraries/spirv_cross/spirv_cfg.hpp +++ b/src/libraries/spirv_cross/spirv_cfg.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 Arm Limited + * Copyright 2016-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_common.hpp b/src/libraries/spirv_cross/spirv_common.hpp index f25486a6f..252d03295 100644 --- a/src/libraries/spirv_cross/spirv_common.hpp +++ b/src/libraries/spirv_cross/spirv_common.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -209,9 +209,12 @@ inline std::string convert_to_string(const T &t) #define SPIRV_CROSS_FLT_FMT "%.32g" #endif -#ifdef _MSC_VER -// sprintf warning. -// We cannot rely on snprintf existing because, ..., MSVC. +// Disable sprintf and strcat warnings. +// We cannot rely on snprintf and family existing because, ..., MSVC. +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif @@ -259,7 +262,9 @@ inline std::string convert_to_string(double t, char locale_radix_point) return buf; } -#ifdef _MSC_VER +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) #pragma warning(pop) #endif @@ -525,7 +530,8 @@ struct SPIRType : IVariant Image, SampledImage, Sampler, - AccelerationStructureNV, + AccelerationStructure, + RayQuery, // Keep internal types at the end. ControlPointArray, @@ -638,6 +644,7 @@ struct SPIREntryPoint uint32_t invocations = 0; uint32_t output_vertices = 0; spv::ExecutionModel model = spv::ExecutionModelMax; + bool geometry_passthrough = false; }; struct SPIRExpression : IVariant @@ -938,6 +945,11 @@ struct SPIRFunction : IVariant // Intentionally not a small vector, this one is rare, and std::function can be large. Vector> fixup_hooks_in; + // On function entry, make sure to copy a constant array into thread addr space to work around + // the case where we are passing a constant array by value to a function on backends which do not + // consider arrays value types. + SmallVector constant_arrays_needed_on_stack; + bool active = false; bool flush_undeclared = true; bool do_combined_parameters = true; @@ -1570,6 +1582,8 @@ struct Meta uint32_t set = 0; uint32_t binding = 0; uint32_t offset = 0; + uint32_t xfb_buffer = 0; + uint32_t xfb_stride = 0; uint32_t array_stride = 0; uint32_t matrix_stride = 0; uint32_t input_attachment = 0; @@ -1694,6 +1708,62 @@ static inline bool opcode_is_sign_invariant(spv::Op opcode) return false; } } + +struct SetBindingPair +{ + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const SetBindingPair &other) const + { + return desc_set == other.desc_set && binding == other.binding; + } + + inline bool operator<(const SetBindingPair &other) const + { + return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); + } +}; + +struct StageSetBinding +{ + spv::ExecutionModel model; + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const StageSetBinding &other) const + { + return model == other.model && desc_set == other.desc_set && binding == other.binding; + } +}; + +struct InternalHasher +{ + inline size_t operator()(const SetBindingPair &value) const + { + // Quality of hash doesn't really matter here. + auto hash_set = std::hash()(value.desc_set); + auto hash_binding = std::hash()(value.binding); + return (hash_set * 0x10001b31) ^ hash_binding; + } + + inline size_t operator()(const StageSetBinding &value) const + { + // Quality of hash doesn't really matter here. + auto hash_model = std::hash()(value.model); + auto hash_set = std::hash()(value.desc_set); + auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set; + return (tmp_hash * 0x10001b31) ^ value.binding; + } +}; + +// Special constant used in a {MSL,HLSL}ResourceBinding desc_set +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantDescriptorSet = ~(0u); + +// Special constant used in a {MSL,HLSL}ResourceBinding binding +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantBinding = 0; } // namespace SPIRV_CROSS_NAMESPACE namespace std diff --git a/src/libraries/spirv_cross/spirv_cpp.cpp b/src/libraries/spirv_cross/spirv_cpp.cpp index 25966b32a..53094d4e1 100644 --- a/src/libraries/spirv_cross/spirv_cpp.cpp +++ b/src/libraries/spirv_cross/spirv_cpp.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_cpp.hpp b/src/libraries/spirv_cross/spirv_cpp.hpp index 4c20aa37b..e78d0369f 100644 --- a/src/libraries/spirv_cross/spirv_cpp.hpp +++ b/src/libraries/spirv_cross/spirv_cpp.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_cross.cpp b/src/libraries/spirv_cross/spirv_cross.cpp index 6e2cc95fa..6fd31c885 100644 --- a/src/libraries/spirv_cross/spirv_cross.cpp +++ b/src/libraries/spirv_cross/spirv_cross.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -871,7 +871,7 @@ ShaderResources Compiler::get_shader_resources(const unordered_set * res.atomic_counters.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); } // Acceleration structures - else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::AccelerationStructureNV) + else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::AccelerationStructure) { res.acceleration_structures.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); } diff --git a/src/libraries/spirv_cross/spirv_cross.hpp b/src/libraries/spirv_cross/spirv_cross.hpp index 92379aef4..373f99762 100644 --- a/src/libraries/spirv_cross/spirv_cross.hpp +++ b/src/libraries/spirv_cross/spirv_cross.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_cross_c.cpp b/src/libraries/spirv_cross/spirv_cross_c.cpp index b590fe801..201098882 100644 --- a/src/libraries/spirv_cross/spirv_cross_c.cpp +++ b/src/libraries/spirv_cross/spirv_cross_c.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019 Hans-Kristian Arntzen + * Copyright 2019-2020 Hans-Kristian Arntzen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -420,6 +420,12 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c case SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES: options->glsl.emit_line_directives = value != 0; break; + case SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION: + options->glsl.enable_storage_image_qualifier_deduction = value != 0; + break; + case SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES: + options->glsl.force_zero_initialized_variables = value != 0; + break; case SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE: options->glsl.vertex.support_nonzero_base_instance = value != 0; @@ -471,6 +477,14 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c case SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE: options->hlsl.support_nonzero_base_vertex_base_instance = value != 0; break; + + case SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV: + options->hlsl.force_storage_buffer_as_uav = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV: + options->hlsl.nonwritable_uav_texture_as_srv = value != 0; + break; #endif #if SPIRV_CROSS_C_API_MSL @@ -593,6 +607,30 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c case SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING: options->msl.enable_decoration_binding = value != 0; break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES: + options->msl.force_active_argument_buffer_resources = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS: + options->msl.force_native_arrays = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK: + options->msl.enable_frag_output_mask = value; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN: + options->msl.enable_frag_depth_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN: + options->msl.enable_frag_stencil_ref_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING: + options->msl.enable_clip_distance_user_varying = value != 0; + break; #endif default: @@ -689,6 +727,23 @@ spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_vari #endif } +spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + return static_cast(compiler->compiler.get())->variable_is_depth_or_compare(id) ? SPVC_TRUE : SPVC_FALSE; +#else + (void)id; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_FALSE; +#endif +} + spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler, const spvc_hlsl_root_constants *constant_info, size_t count) @@ -789,6 +844,60 @@ spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler #endif } +spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + HLSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.cbv.register_binding = binding->cbv.register_binding; + bind.cbv.register_space = binding->cbv.register_space; + bind.uav.register_binding = binding->uav.register_binding; + bind.uav.register_space = binding->uav.register_space; + bind.srv.register_binding = binding->srv.register_binding; + bind.srv.register_space = binding->srv.register_space; + bind.sampler.register_binding = binding->sampler.register_binding; + bind.sampler.register_space = binding->sampler.register_space; + hlsl.add_hlsl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set, + unsigned binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + return hlsl.is_hlsl_resource_binding_used(static_cast(model), set, binding) ? SPVC_TRUE : + SPVC_FALSE; +#else + (void)model; + (void)set; + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; +#endif +} + spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler) { #if SPIRV_CROSS_C_API_MSL @@ -971,6 +1080,26 @@ spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigne #endif } +spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_inline_uniform_block(desc_set, binding); + return SPVC_SUCCESS; +#else + (void)binding; + (void)desc_set; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set) { #if SPIRV_CROSS_C_API_MSL @@ -2157,6 +2286,26 @@ void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding) #endif } +void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + HLSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->cbv.register_binding = binding_default.cbv.register_binding; + binding->cbv.register_space = binding_default.cbv.register_space; + binding->srv.register_binding = binding_default.srv.register_binding; + binding->srv.register_space = binding_default.srv.register_space; + binding->uav.register_binding = binding_default.uav.register_binding; + binding->uav.register_space = binding_default.uav.register_space; + binding->sampler.register_binding = binding_default.sampler.register_binding; + binding->sampler.register_space = binding_default.sampler.register_space; + binding->stage = static_cast(binding_default.stage); +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler) { #if SPIRV_CROSS_C_API_MSL diff --git a/src/libraries/spirv_cross/spirv_cross_c.h b/src/libraries/spirv_cross/spirv_cross_c.h index a3ad84be1..0c2183969 100644 --- a/src/libraries/spirv_cross/spirv_cross_c.h +++ b/src/libraries/spirv_cross/spirv_cross_c.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 Hans-Kristian Arntzen + * Copyright 2019-2020 Hans-Kristian Arntzen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ extern "C" { /* Bumped if ABI or API breaks backwards compatibility. */ #define SPVC_C_API_VERSION_MAJOR 0 /* Bumped if APIs or enumerations are added in a backwards compatible way. */ -#define SPVC_C_API_VERSION_MINOR 21 +#define SPVC_C_API_VERSION_MINOR 33 /* Bumped if internal implementation details change. */ #define SPVC_C_API_VERSION_PATCH 0 @@ -210,6 +210,7 @@ typedef enum spvc_resource_type SPVC_RESOURCE_TYPE_SEPARATE_IMAGE = 10, SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS = 11, SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE = 12, + SPVC_RESOURCE_TYPE_RAY_QUERY = 13, SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff } spvc_resource_type; @@ -469,6 +470,7 @@ SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycb /* Maps to C++ API. */ typedef enum spvc_hlsl_binding_flag_bits { + SPVC_HLSL_BINDING_AUTO_NONE_BIT = 0, SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, @@ -478,6 +480,31 @@ typedef enum spvc_hlsl_binding_flag_bits } spvc_hlsl_binding_flag_bits; typedef unsigned spvc_hlsl_binding_flags; +#define SPVC_HLSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_HLSL_PUSH_CONSTANT_BINDING (0) + +/* Maps to C++ API. */ +typedef struct spvc_hlsl_resource_binding_mapping +{ + unsigned register_space; + unsigned register_binding; +} spvc_hlsl_resource_binding_mapping; + +typedef struct spvc_hlsl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + + spvc_hlsl_resource_binding_mapping cbv, uav, srv, sampler; +} spvc_hlsl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding); + /* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */ typedef enum spvc_compiler_option { @@ -545,6 +572,21 @@ typedef enum spvc_compiler_option SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH = 47 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY = 48 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING = 49 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES = 50 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS = 51 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION = 52 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV = 53 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES = 54 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV = 55 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK = 56 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN = 57 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN = 58 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING = 59 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff } spvc_compiler_option; @@ -606,6 +648,8 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext); SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id); + /* * HLSL specifics. * Maps to C++ API. @@ -621,6 +665,13 @@ SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, spvc_hlsl_binding_flags flags); +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding); +SPVC_PUBLIC_API spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); + /* * MSL specifics. * Maps to C++ API. @@ -657,6 +708,8 @@ SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding_second SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding); + /* * Reflect resources. * Maps almost 1:1 to C++ API. diff --git a/src/libraries/spirv_cross/spirv_cross_containers.hpp b/src/libraries/spirv_cross/spirv_cross_containers.hpp index 31a8abbd0..f5760a0e0 100644 --- a/src/libraries/spirv_cross/spirv_cross_containers.hpp +++ b/src/libraries/spirv_cross/spirv_cross_containers.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019 Hans-Kristian Arntzen + * Copyright 2019-2020 Hans-Kristian Arntzen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -85,72 +87,72 @@ template class VectorView { public: - T &operator[](size_t i) + T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT { return ptr[i]; } - const T &operator[](size_t i) const + const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT { return ptr[i]; } - bool empty() const + bool empty() const SPIRV_CROSS_NOEXCEPT { return buffer_size == 0; } - size_t size() const + size_t size() const SPIRV_CROSS_NOEXCEPT { return buffer_size; } - T *data() + T *data() SPIRV_CROSS_NOEXCEPT { return ptr; } - const T *data() const + const T *data() const SPIRV_CROSS_NOEXCEPT { return ptr; } - T *begin() + T *begin() SPIRV_CROSS_NOEXCEPT { return ptr; } - T *end() + T *end() SPIRV_CROSS_NOEXCEPT { return ptr + buffer_size; } - const T *begin() const + const T *begin() const SPIRV_CROSS_NOEXCEPT { return ptr; } - const T *end() const + const T *end() const SPIRV_CROSS_NOEXCEPT { return ptr + buffer_size; } - T &front() + T &front() SPIRV_CROSS_NOEXCEPT { return ptr[0]; } - const T &front() const + const T &front() const SPIRV_CROSS_NOEXCEPT { return ptr[0]; } - T &back() + T &back() SPIRV_CROSS_NOEXCEPT { return ptr[buffer_size - 1]; } - const T &back() const + const T &back() const SPIRV_CROSS_NOEXCEPT { return ptr[buffer_size - 1]; } @@ -194,14 +196,13 @@ template class SmallVector : public VectorView { public: - SmallVector() + SmallVector() SPIRV_CROSS_NOEXCEPT { this->ptr = stack_storage.data(); buffer_capacity = N; } - SmallVector(const T *arg_list_begin, const T *arg_list_end) - : SmallVector() + SmallVector(const T *arg_list_begin, const T *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector() { auto count = size_t(arg_list_end - arg_list_begin); reserve(count); @@ -245,14 +246,16 @@ public: return *this; } - SmallVector(const SmallVector &other) - : SmallVector() + SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector() { *this = other; } - SmallVector &operator=(const SmallVector &other) + SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT { + if (this == &other) + return *this; + clear(); reserve(other.buffer_size); for (size_t i = 0; i < other.buffer_size; i++) @@ -261,8 +264,7 @@ public: return *this; } - explicit SmallVector(size_t count) - : SmallVector() + explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector() { resize(count); } @@ -274,28 +276,28 @@ public: free(this->ptr); } - void clear() + void clear() SPIRV_CROSS_NOEXCEPT { for (size_t i = 0; i < this->buffer_size; i++) this->ptr[i].~T(); this->buffer_size = 0; } - void push_back(const T &t) + void push_back(const T &t) SPIRV_CROSS_NOEXCEPT { reserve(this->buffer_size + 1); new (&this->ptr[this->buffer_size]) T(t); this->buffer_size++; } - void push_back(T &&t) + void push_back(T &&t) SPIRV_CROSS_NOEXCEPT { reserve(this->buffer_size + 1); new (&this->ptr[this->buffer_size]) T(std::move(t)); this->buffer_size++; } - void pop_back() + void pop_back() SPIRV_CROSS_NOEXCEPT { // Work around false positive warning on GCC 8.3. // Calling pop_back on empty vector is undefined. @@ -304,15 +306,22 @@ public: } template - void emplace_back(Ts &&... ts) + void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT { reserve(this->buffer_size + 1); new (&this->ptr[this->buffer_size]) T(std::forward(ts)...); this->buffer_size++; } - void reserve(size_t count) + void reserve(size_t count) SPIRV_CROSS_NOEXCEPT { + if ((count > std::numeric_limits::max() / sizeof(T)) || + (count > std::numeric_limits::max() / 2)) + { + // Only way this should ever happen is with garbage input, terminate. + std::terminate(); + } + if (count > buffer_capacity) { size_t target_capacity = buffer_capacity; @@ -321,14 +330,17 @@ public: if (target_capacity < N) target_capacity = N; + // Need to ensure there is a POT value of target capacity which is larger than count, + // otherwise this will overflow. while (target_capacity < count) target_capacity <<= 1u; T *new_buffer = target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. if (!new_buffer) - SPIRV_CROSS_THROW("Out of memory."); + std::terminate(); // In case for some reason two allocations both come from same stack. if (new_buffer != this->ptr) @@ -348,7 +360,7 @@ public: } } - void insert(T *itr, const T *insert_begin, const T *insert_end) + void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT { auto count = size_t(insert_end - insert_begin); if (itr == this->end()) @@ -374,8 +386,10 @@ public: // Need to allocate new buffer. Move everything to a new buffer. T *new_buffer = target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. if (!new_buffer) - SPIRV_CROSS_THROW("Out of memory."); + std::terminate(); // First, move elements from source buffer to new buffer. // We don't deal with types which can throw in move constructor. @@ -447,19 +461,19 @@ public: } } - void insert(T *itr, const T &value) + void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT { insert(itr, &value, &value + 1); } - T *erase(T *itr) + T *erase(T *itr) SPIRV_CROSS_NOEXCEPT { std::move(itr + 1, this->end(), itr); this->ptr[--this->buffer_size].~T(); return itr; } - void erase(T *start_erase, T *end_erase) + void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT { if (end_erase == this->end()) { @@ -473,7 +487,7 @@ public: } } - void resize(size_t new_size) + void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT { if (new_size < this->buffer_size) { diff --git a/src/libraries/spirv_cross/spirv_cross_error_handling.hpp b/src/libraries/spirv_cross/spirv_cross_error_handling.hpp index a8b2b1530..fa90610b5 100644 --- a/src/libraries/spirv_cross/spirv_cross_error_handling.hpp +++ b/src/libraries/spirv_cross/spirv_cross_error_handling.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp b/src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp index 4b65b4e0b..655713f8c 100644 --- a/src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp +++ b/src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019 Arm Limited + * Copyright 2018-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -266,6 +266,14 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument) dec.offset = argument; break; + case DecorationXfbBuffer: + dec.xfb_buffer = argument; + break; + + case DecorationXfbStride: + dec.xfb_stride = argument; + break; + case DecorationArrayStride: dec.array_stride = argument; break; @@ -337,6 +345,14 @@ void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decor dec.offset = argument; break; + case DecorationXfbBuffer: + dec.xfb_buffer = argument; + break; + + case DecorationXfbStride: + dec.xfb_stride = argument; + break; + case DecorationSpecId: dec.spec_id = argument; break; @@ -450,6 +466,10 @@ uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const return dec.component; case DecorationOffset: return dec.offset; + case DecorationXfbBuffer: + return dec.xfb_buffer; + case DecorationXfbStride: + return dec.xfb_stride; case DecorationBinding: return dec.binding; case DecorationDescriptorSet: @@ -514,6 +534,14 @@ void ParsedIR::unset_decoration(ID id, Decoration decoration) dec.offset = 0; break; + case DecorationXfbBuffer: + dec.xfb_buffer = 0; + break; + + case DecorationXfbStride: + dec.xfb_stride = 0; + break; + case DecorationBinding: dec.binding = 0; break; @@ -584,6 +612,10 @@ uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration d return dec.binding; case DecorationOffset: return dec.offset; + case DecorationXfbBuffer: + return dec.xfb_buffer; + case DecorationXfbStride: + return dec.xfb_stride; case DecorationSpecId: return dec.spec_id; case DecorationIndex: @@ -672,6 +704,14 @@ void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration dec dec.offset = 0; break; + case DecorationXfbBuffer: + dec.xfb_buffer = 0; + break; + + case DecorationXfbStride: + dec.xfb_stride = 0; + break; + case DecorationSpecId: dec.spec_id = 0; break; @@ -814,4 +854,57 @@ ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_ return *this; } +void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set) +{ + auto &constant_type = get(type); + + if (constant_type.pointer) + { + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto &constant = variant_set(ids[id], type); + constant.self = id; + constant.make_null(constant_type); + } + else if (!constant_type.array.empty()) + { + assert(constant_type.parent_type); + uint32_t parent_id = increase_bound_by(1); + make_constant_null(parent_id, constant_type.parent_type, add_to_typed_id_set); + + if (!constant_type.array_size_literal.back()) + SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal."); + + SmallVector elements(constant_type.array.back()); + for (uint32_t i = 0; i < constant_type.array.back(); i++) + elements[i] = parent_id; + + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + variant_set(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id; + } + else if (!constant_type.member_types.empty()) + { + uint32_t member_ids = increase_bound_by(uint32_t(constant_type.member_types.size())); + SmallVector elements(constant_type.member_types.size()); + for (uint32_t i = 0; i < constant_type.member_types.size(); i++) + { + make_constant_null(member_ids + i, constant_type.member_types[i], add_to_typed_id_set); + elements[i] = member_ids + i; + } + + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + variant_set(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id; + } + else + { + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto &constant = variant_set(ids[id], type); + constant.self = id; + constant.make_null(constant_type); + } +} + } // namespace SPIRV_CROSS_NAMESPACE diff --git a/src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp b/src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp index 97bc9ea69..4880c8419 100644 --- a/src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp +++ b/src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019 Arm Limited + * Copyright 2018-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -206,6 +206,8 @@ public: return empty_string; } + void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set); + private: template T &get(uint32_t id) diff --git a/src/libraries/spirv_cross/spirv_cross_util.cpp b/src/libraries/spirv_cross/spirv_cross_util.cpp index 6ab5d2645..cfad676ec 100644 --- a/src/libraries/spirv_cross/spirv_cross_util.cpp +++ b/src/libraries/spirv_cross/spirv_cross_util.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_cross_util.hpp b/src/libraries/spirv_cross/spirv_cross_util.hpp index 7c4030b0b..708b7cb56 100644 --- a/src/libraries/spirv_cross/spirv_cross_util.hpp +++ b/src/libraries/spirv_cross/spirv_cross_util.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/libraries/spirv_cross/spirv_glsl.cpp b/src/libraries/spirv_cross/spirv_glsl.cpp index 20cb624ac..60460d811 100644 --- a/src/libraries/spirv_cross/spirv_glsl.cpp +++ b/src/libraries/spirv_cross/spirv_glsl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -370,6 +370,12 @@ void CompilerGLSL::remap_pls_variables() } } +void CompilerGLSL::remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location) +{ + subpass_to_framebuffer_fetch_attachment.push_back({ input_attachment_index, color_location }); + inout_color_attachments.insert(color_location); +} + void CompilerGLSL::find_static_extensions() { ir.for_each_typed_id([&](uint32_t, const SPIRType &type) { @@ -455,7 +461,20 @@ void CompilerGLSL::find_static_extensions() } if (!pls_inputs.empty() || !pls_outputs.empty()) + { + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Can only use GL_EXT_shader_pixel_local_storage in fragment shaders."); require_extension_internal("GL_EXT_shader_pixel_local_storage"); + } + + if (!inout_color_attachments.empty()) + { + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Can only use GL_EXT_shader_framebuffer_fetch in fragment shaders."); + if (options.vulkan_semantics) + SPIRV_CROSS_THROW("Cannot use EXT_shader_framebuffer_fetch in Vulkan GLSL."); + require_extension_internal("GL_EXT_shader_framebuffer_fetch"); + } if (options.separate_shader_objects && !options.es && options.version < 410) require_extension_internal("GL_ARB_separate_shader_objects"); @@ -475,27 +494,35 @@ void CompilerGLSL::find_static_extensions() SPIRV_CROSS_THROW("Only Logical and PhysicalStorageBuffer64EXT addressing models are supported."); } - // Check for nonuniform qualifier. + // Check for nonuniform qualifier and passthrough. // Instead of looping over all decorations to find this, just look at capabilities. for (auto &cap : ir.declared_capabilities) { - bool nonuniform_indexing = false; switch (cap) { case CapabilityShaderNonUniformEXT: + if (!options.vulkan_semantics) + require_extension_internal("GL_NV_gpu_shader5"); + else + require_extension_internal("GL_EXT_nonuniform_qualifier"); + break; case CapabilityRuntimeDescriptorArrayEXT: if (!options.vulkan_semantics) SPIRV_CROSS_THROW("GL_EXT_nonuniform_qualifier is only supported in Vulkan GLSL."); require_extension_internal("GL_EXT_nonuniform_qualifier"); - nonuniform_indexing = true; + break; + + case CapabilityGeometryShaderPassthroughNV: + if (execution.model == ExecutionModelGeometry) + { + require_extension_internal("GL_NV_geometry_shader_passthrough"); + execution.geometry_passthrough = true; + } break; default: break; } - - if (nonuniform_indexing) - break; } } @@ -503,6 +530,11 @@ string CompilerGLSL::compile() { if (options.vulkan_semantics) backend.allow_precision_qualifiers = true; + else + { + // only NV_gpu_shader5 supports divergent indexing on OpenGL, and it does so without extra qualifiers + backend.nonuniform_qualifier = ""; + } backend.force_gl_in_out_block = true; backend.supports_extensions = true; backend.use_array_constructor = true; @@ -516,6 +548,8 @@ string CompilerGLSL::compile() update_active_builtins(); analyze_image_and_sampler_usage(); analyze_interlocked_resource_usage(); + if (!inout_color_attachments.empty()) + emit_inout_fragment_outputs_copy_to_subpass_inputs(); // Shaders might cast unrelated data to pointers of non-block types. // Find all such instances and make sure we can cast the pointers to a synthesized block type. @@ -710,7 +744,6 @@ void CompilerGLSL::emit_header() switch (execution.model) { case ExecutionModelGeometry: - outputs.push_back(join("max_vertices = ", execution.output_vertices)); if ((execution.flags.get(ExecutionModeInvocations)) && execution.invocations != 1) inputs.push_back(join("invocations = ", execution.invocations)); if (execution.flags.get(ExecutionModeInputPoints)) @@ -723,12 +756,18 @@ void CompilerGLSL::emit_header() inputs.push_back("triangles"); if (execution.flags.get(ExecutionModeInputTrianglesAdjacency)) inputs.push_back("triangles_adjacency"); - if (execution.flags.get(ExecutionModeOutputTriangleStrip)) - outputs.push_back("triangle_strip"); - if (execution.flags.get(ExecutionModeOutputPoints)) - outputs.push_back("points"); - if (execution.flags.get(ExecutionModeOutputLineStrip)) - outputs.push_back("line_strip"); + + if (!execution.geometry_passthrough) + { + // For passthrough, these are implies and cannot be declared in shader. + outputs.push_back(join("max_vertices = ", execution.output_vertices)); + if (execution.flags.get(ExecutionModeOutputTriangleStrip)) + outputs.push_back("triangle_strip"); + if (execution.flags.get(ExecutionModeOutputPoints)) + outputs.push_back("points"); + if (execution.flags.get(ExecutionModeOutputLineStrip)) + outputs.push_back("line_strip"); + } break; case ExecutionModelTessellationControl: @@ -947,6 +986,9 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) SmallVector attr; + if (has_member_decoration(type.self, index, DecorationPassthroughNV)) + attr.push_back("passthrough"); + // We can only apply layouts on members in block interfaces. // This is a bit problematic because in SPIR-V decorations are applied on the struct types directly. // This is not supported on GLSL, so we have to make the assumption that if a struct within our buffer block struct @@ -992,6 +1034,8 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) if (has_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset) && dec.decoration_flags.get(DecorationOffset)) attr.push_back(join("offset = ", dec.offset)); + else if (type.storage == StorageClassOutput && dec.decoration_flags.get(DecorationOffset)) + attr.push_back(join("xfb_offset = ", dec.offset)); if (attr.empty()) return ""; @@ -1506,12 +1550,17 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) if (is_legacy()) return ""; + if (subpass_input_is_framebuffer_fetch(var.self)) + return ""; + SmallVector attr; - auto &dec = ir.meta[var.self].decoration; auto &type = get(var.basetype); - auto &flags = dec.decoration_flags; - auto typeflags = ir.meta[type.self].decoration.decoration_flags; + auto &flags = get_decoration_bitset(var.self); + auto &typeflags = get_decoration_bitset(type.self); + + if (flags.get(DecorationPassthroughNV)) + attr.push_back("passthrough"); if (options.vulkan_semantics && var.storage == StorageClassPushConstant) attr.push_back("push_constant"); @@ -1526,7 +1575,7 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) if (options.vulkan_semantics) { if (flags.get(DecorationInputAttachmentIndex)) - attr.push_back(join("input_attachment_index = ", dec.input_attachment)); + attr.push_back(join("input_attachment_index = ", get_decoration(var.self, DecorationInputAttachmentIndex))); } bool is_block = has_decoration(type.self, DecorationBlock); @@ -1539,33 +1588,103 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) // If our members have location decorations, we don't need to // emit location decorations at the top as well (looks weird). if (!combined_decoration.get(DecorationLocation)) - attr.push_back(join("location = ", dec.location)); + attr.push_back(join("location = ", get_decoration(var.self, DecorationLocation))); + } + + // Transform feedback + bool uses_enhanced_layouts = false; + if (is_block && var.storage == StorageClassOutput) + { + // For blocks, there is a restriction where xfb_stride/xfb_buffer must only be declared on the block itself, + // since all members must match the same xfb_buffer. The only thing we will declare for members of the block + // is the xfb_offset. + uint32_t member_count = uint32_t(type.member_types.size()); + bool have_xfb_buffer_stride = false; + bool have_any_xfb_offset = false; + uint32_t xfb_stride = 0, xfb_buffer = 0; + + if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride)) + { + have_xfb_buffer_stride = true; + xfb_buffer = get_decoration(var.self, DecorationXfbBuffer); + xfb_stride = get_decoration(var.self, DecorationXfbStride); + } + + // Verify that none of the members violate our assumption. + for (uint32_t i = 0; i < member_count; i++) + { + // Only members with an Offset decoration participate in XFB. + if (!has_member_decoration(type.self, i, DecorationOffset)) + continue; + have_any_xfb_offset = true; + + if (has_member_decoration(type.self, i, DecorationXfbBuffer)) + { + uint32_t buffer_index = get_member_decoration(type.self, i, DecorationXfbBuffer); + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + } + + if (has_member_decoration(type.self, i, DecorationXfbStride)) + { + uint32_t stride = get_member_decoration(type.self, i, DecorationXfbStride); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbStride mismatch."); + have_xfb_buffer_stride = true; + xfb_stride = stride; + } + } + + if (have_xfb_buffer_stride && have_any_xfb_offset) + { + attr.push_back(join("xfb_buffer = ", xfb_buffer)); + attr.push_back(join("xfb_stride = ", xfb_stride)); + uses_enhanced_layouts = true; + } + } + else if (var.storage == StorageClassOutput && flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride) && + flags.get(DecorationOffset)) + { + // XFB for standalone variables, we can emit all decorations. + attr.push_back(join("xfb_buffer = ", get_decoration(var.self, DecorationXfbBuffer))); + attr.push_back(join("xfb_stride = ", get_decoration(var.self, DecorationXfbStride))); + attr.push_back(join("xfb_offset = ", get_decoration(var.self, DecorationOffset))); + uses_enhanced_layouts = true; } // Can only declare Component if we can declare location. if (flags.get(DecorationComponent) && can_use_io_location(var.storage, is_block)) + { + uses_enhanced_layouts = true; + attr.push_back(join("component = ", get_decoration(var.self, DecorationComponent))); + } + + if (uses_enhanced_layouts) { if (!options.es) { if (options.version < 440 && options.version >= 140) require_extension_internal("GL_ARB_enhanced_layouts"); else if (options.version < 140) - SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40."); - attr.push_back(join("component = ", dec.component)); + SPIRV_CROSS_THROW("GL_ARB_enhanced_layouts is not supported in targets below GLSL 1.40."); + if (!options.es && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); } - else - SPIRV_CROSS_THROW("Component decoration is not supported in ES targets."); + else if (options.es) + SPIRV_CROSS_THROW("GL_ARB_enhanced_layouts is not supported in ESSL."); } if (flags.get(DecorationIndex)) - attr.push_back(join("index = ", dec.index)); + attr.push_back(join("index = ", get_decoration(var.self, DecorationIndex))); // Do not emit set = decoration in regular GLSL output, but // we need to preserve it in Vulkan GLSL mode. if (var.storage != StorageClassPushConstant && var.storage != StorageClassShaderRecordBufferNV) { if (flags.get(DecorationDescriptorSet) && options.vulkan_semantics) - attr.push_back(join("set = ", dec.set)); + attr.push_back(join("set = ", get_decoration(var.self, DecorationDescriptorSet))); } bool push_constant_block = options.vulkan_semantics && var.storage == StorageClassPushConstant; @@ -1595,10 +1714,10 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) can_use_binding = false; if (can_use_binding && flags.get(DecorationBinding)) - attr.push_back(join("binding = ", dec.binding)); + attr.push_back(join("binding = ", get_decoration(var.self, DecorationBinding))); - if (flags.get(DecorationOffset)) - attr.push_back(join("offset = ", dec.offset)); + if (var.storage != StorageClassOutput && flags.get(DecorationOffset)) + attr.push_back(join("offset = ", get_decoration(var.self, DecorationOffset))); // Instead of adding explicit offsets for every element here, just assume we're using std140 or std430. // If SPIR-V does not comply with either layout, we cannot really work around it. @@ -1946,12 +2065,22 @@ const char *CompilerGLSL::to_storage_qualifiers_glsl(const SPIRVariable &var) { auto &execution = get_entry_point(); + if (subpass_input_is_framebuffer_fetch(var.self)) + return ""; + if (var.storage == StorageClassInput || var.storage == StorageClassOutput) { if (is_legacy() && execution.model == ExecutionModelVertex) return var.storage == StorageClassInput ? "attribute " : "varying "; else if (is_legacy() && execution.model == ExecutionModelFragment) return "varying "; // Fragment outputs are renamed so they never hit this case. + else if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput) + { + if (inout_color_attachments.count(get_decoration(var.self, DecorationLocation)) != 0) + return "inout "; + else + return "out "; + } else return var.storage == StorageClassInput ? "in " : "out "; } @@ -2144,7 +2273,7 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var) void CompilerGLSL::emit_uniform(const SPIRVariable &var) { auto &type = get(var.basetype); - if (type.basetype == SPIRType::Image && type.image.sampled == 2) + if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData) { if (!options.es && options.version < 420) require_extension_internal("GL_ARB_shader_image_load_store"); @@ -2228,6 +2357,36 @@ void CompilerGLSL::emit_entry_point_declarations() { } +void CompilerGLSL::replace_illegal_names(const unordered_set &keywords) +{ + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (is_hidden_variable(var)) + return; + + auto *meta = ir.find_meta(var.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (m.alias.compare(0, 3, "gl_") == 0 || keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + }); + + ir.for_each_typed_id([&](uint32_t, const SPIRType &type) { + auto *meta = ir.find_meta(type.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (m.alias.compare(0, 3, "gl_") == 0 || keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + + for (auto &memb : meta->members) + if (memb.alias.compare(0, 3, "gl_") == 0 || keywords.find(memb.alias) != end(keywords)) + memb.alias = join("_", memb.alias); + }); +} + void CompilerGLSL::replace_illegal_names() { // clang-format off @@ -2282,14 +2441,7 @@ void CompilerGLSL::replace_illegal_names() }; // clang-format on - ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { - if (!is_hidden_variable(var)) - { - auto &m = ir.meta[var.self].decoration; - if (m.alias.compare(0, 3, "gl_") == 0 || keywords.find(m.alias) != end(keywords)) - m.alias = join("_", m.alias); - } - }); + replace_illegal_names(keywords); } void CompilerGLSL::replace_fragment_output(SPIRVariable &var) @@ -2396,11 +2548,14 @@ void CompilerGLSL::emit_pls() void CompilerGLSL::fixup_image_load_store_access() { + if (!options.enable_storage_image_qualifier_deduction) + return; + ir.for_each_typed_id([&](uint32_t var, const SPIRVariable &) { auto &vartype = expression_type(var); - if (vartype.basetype == SPIRType::Image) + if (vartype.basetype == SPIRType::Image && vartype.image.sampled == 2) { - // Older glslangValidator does not emit required qualifiers here. + // Very old glslangValidator and HLSL compilers do not emit required qualifiers here. // Solve this by making the image access as restricted as possible and loosen up if we need to. // If any no-read/no-write flags are actually set, assume that the compiler knows what it's doing. @@ -2414,6 +2569,52 @@ void CompilerGLSL::fixup_image_load_store_access() }); } +static bool is_block_builtin(BuiltIn builtin) +{ + return builtin == BuiltInPosition || builtin == BuiltInPointSize || builtin == BuiltInClipDistance || + builtin == BuiltInCullDistance; +} + +bool CompilerGLSL::should_force_emit_builtin_block(StorageClass storage) +{ + // If the builtin block uses XFB, we need to force explicit redeclaration of the builtin block. + + if (storage != StorageClassOutput) + return false; + bool should_force = false; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (should_force) + return; + + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + if (var.storage == storage && block && is_builtin_variable(var)) + { + uint32_t member_count = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationBuiltIn) && + is_block_builtin(BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn))) && + has_member_decoration(type.self, i, DecorationOffset)) + { + should_force = true; + } + } + } + else if (var.storage == storage && !block && is_builtin_variable(var)) + { + if (is_block_builtin(BuiltIn(get_decoration(type.self, DecorationBuiltIn))) && + has_decoration(var.self, DecorationOffset)) + { + should_force = true; + } + } + }); + + return should_force; +} + void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model) { Bitset emitted_builtins; @@ -2427,6 +2628,11 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo uint32_t cull_distance_size = 0; uint32_t clip_distance_size = 0; + bool have_xfb_buffer_stride = false; + bool have_any_xfb_offset = false; + uint32_t xfb_stride = 0, xfb_buffer = 0; + std::unordered_map builtin_xfb_offsets; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { auto &type = this->get(var.basetype); bool block = has_decoration(type.self, DecorationBlock); @@ -2444,9 +2650,29 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo cull_distance_size = this->get(type.member_types[index]).array.front(); else if (m.builtin_type == BuiltInClipDistance) clip_distance_size = this->get(type.member_types[index]).array.front(); + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationOffset)) + { + have_any_xfb_offset = true; + builtin_xfb_offsets[m.builtin_type] = m.offset; + } } index++; } + + if (storage == StorageClassOutput && has_decoration(var.self, DecorationXfbBuffer) && + has_decoration(var.self, DecorationXfbStride)) + { + uint32_t buffer_index = get_decoration(var.self, DecorationXfbBuffer); + uint32_t stride = get_decoration(var.self, DecorationXfbStride); + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + xfb_stride = stride; + } } else if (var.storage == storage && !block && is_builtin_variable(var)) { @@ -2459,6 +2685,22 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo cull_distance_size = type.array.front(); else if (m.builtin_type == BuiltInClipDistance) clip_distance_size = type.array.front(); + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationXfbStride) && + m.decoration_flags.get(DecorationXfbBuffer) && m.decoration_flags.get(DecorationOffset)) + { + have_any_xfb_offset = true; + builtin_xfb_offsets[m.builtin_type] = m.offset; + uint32_t buffer_index = m.xfb_buffer; + uint32_t stride = m.xfb_stride; + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + xfb_stride = stride; + } } } @@ -2487,19 +2729,70 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo return; if (storage == StorageClassOutput) - statement("out gl_PerVertex"); + { + if (have_xfb_buffer_stride && have_any_xfb_offset) + { + statement("layout(xfb_buffer = ", xfb_buffer, ", xfb_stride = ", xfb_stride, ") out gl_PerVertex"); + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40."); + if (!options.es && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + } + else if (options.es) + SPIRV_CROSS_THROW("Need GL_ARB_enhanced_layouts for xfb_stride or xfb_buffer."); + } + else + statement("out gl_PerVertex"); + } else - statement("in gl_PerVertex"); + { + // If we have passthrough, there is no way PerVertex cannot be passthrough. + if (get_entry_point().geometry_passthrough) + statement("layout(passthrough) in gl_PerVertex"); + else + statement("in gl_PerVertex"); + } begin_scope(); if (emitted_builtins.get(BuiltInPosition)) - statement("vec4 gl_Position;"); + { + auto itr = builtin_xfb_offsets.find(BuiltInPosition); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") vec4 gl_Position;"); + else + statement("vec4 gl_Position;"); + } + if (emitted_builtins.get(BuiltInPointSize)) - statement("float gl_PointSize;"); + { + auto itr = builtin_xfb_offsets.find(BuiltInPointSize); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_PointSize;"); + else + statement("float gl_PointSize;"); + } + if (emitted_builtins.get(BuiltInClipDistance)) - statement("float gl_ClipDistance[", clip_distance_size, "];"); + { + auto itr = builtin_xfb_offsets.find(BuiltInClipDistance); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_ClipDistance[", clip_distance_size, "];"); + else + statement("float gl_ClipDistance[", clip_distance_size, "];"); + } + if (emitted_builtins.get(BuiltInCullDistance)) - statement("float gl_CullDistance[", cull_distance_size, "];"); + { + auto itr = builtin_xfb_offsets.find(BuiltInCullDistance); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_CullDistance[", cull_distance_size, "];"); + else + statement("float gl_CullDistance[", cull_distance_size, "];"); + } if (builtin_array) { @@ -2523,7 +2816,12 @@ void CompilerGLSL::declare_undefined_values() { bool emitted = false; ir.for_each_typed_id([&](uint32_t, const SPIRUndef &undef) { - statement(variable_decl(this->get(undef.basetype), to_name(undef.self), undef.self), ";"); + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(this->get(undef.basetype))) + initializer = join(" = ", to_zero_initialized_expression(undef.basetype)); + + statement(variable_decl(this->get(undef.basetype), to_name(undef.self), undef.self), initializer, + ";"); emitted = true; }); @@ -2580,6 +2878,16 @@ void CompilerGLSL::emit_resources() break; } } + else if (should_force_emit_builtin_block(StorageClassOutput)) + { + emit_declared_builtin_block(StorageClassOutput, execution.model); + } + else if (execution.geometry_passthrough) + { + // Need to declare gl_in with Passthrough. + // If we're doing passthrough, we cannot emit an output block, so the output block test above will never pass. + emit_declared_builtin_block(StorageClassInput, execution.model); + } else { // Need to redeclare clip/cull distance with explicit size to use them. @@ -2766,9 +3074,18 @@ void CompilerGLSL::emit_resources() ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { auto &type = this->get(var.basetype); + bool is_hidden = is_hidden_variable(var); + + // Unused output I/O variables might still be required to implement framebuffer fetch. + if (var.storage == StorageClassOutput && !is_legacy() && + inout_color_attachments.count(get_decoration(var.self, DecorationLocation)) != 0) + { + is_hidden = false; + } + if (var.storage != StorageClassFunction && type.pointer && (var.storage == StorageClassInput || var.storage == StorageClassOutput) && - interface_variable_exists_in_entry_point(var.self) && !is_hidden_variable(var)) + interface_variable_exists_in_entry_point(var.self) && !is_hidden) { emit_interface_block(var); emitted = true; @@ -2795,7 +3112,15 @@ void CompilerGLSL::emit_resources() if (!variable_is_lut(var)) { add_resource_name(var.self); - statement(variable_decl(var), ";"); + + string initializer; + if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate && + !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var))); + } + + statement(variable_decl(var), initializer, ";"); emitted = true; } } @@ -3441,7 +3766,8 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c) { res = type_to_glsl_constructor(type) + "{ "; } - else if (backend.use_initializer_list && backend.use_typed_initializer_list && !type.array.empty()) + else if (backend.use_initializer_list && backend.use_typed_initializer_list && backend.array_is_value_type && + !type.array.empty()) { res = type_to_glsl_constructor(type) + "({ "; needs_trailing_tracket = true; @@ -4112,7 +4438,12 @@ void CompilerGLSL::emit_uninitialized_temporary(uint32_t result_type, uint32_t r // The result_id has not been made into an expression yet, so use flags interface. add_local_variable_name(result_id); - statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), ";"); + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(result_type)); + + statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), initializer, ";"); } } @@ -4305,6 +4636,26 @@ SPIRType CompilerGLSL::binary_op_bitcast_helper(string &cast_op0, string &cast_o return expected_type; } +bool CompilerGLSL::emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) +{ + // Some bitcasts may require complex casting sequences, and are implemented here. + // Otherwise a simply unary function will do with bitcast_glsl_op. + + auto &output_type = get(result_type); + auto &input_type = expression_type(op0); + string expr; + + if (output_type.basetype == SPIRType::Half && input_type.basetype == SPIRType::Float && input_type.vecsize == 1) + expr = join("unpackFloat2x16(floatBitsToUint(", to_unpacked_expression(op0), "))"); + else if (output_type.basetype == SPIRType::Float && input_type.basetype == SPIRType::Half && input_type.vecsize == 2) + expr = join("uintBitsToFloat(packFloat2x16(", to_unpacked_expression(op0), "))"); + else + return false; + + emit_op(result_type, id, expr, should_forward(op0)); + return true; +} + void CompilerGLSL::emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) { @@ -4464,6 +4815,34 @@ void CompilerGLSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t resu inherit_expression_dependencies(result_id, op2); } +void CompilerGLSL::emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, + uint32_t op1, const char *op, SPIRType::BaseType input_type) +{ + // Special purpose method for implementing clustered subgroup opcodes. + // Main difference is that op1 does not participate in any casting, it needs to be a literal. + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", to_expression(op1), ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", to_expression(op1), ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0)); + inherit_expression_dependencies(result_id, op0); +} + void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) { @@ -6014,6 +6393,11 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i) if (!options.vulkan_semantics) SPIRV_CROSS_THROW("Can only use subgroup operations in Vulkan semantics."); + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + switch (op) { case OpGroupNonUniformElect: @@ -6185,20 +6569,39 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } + +#define GLSL_GROUP_OP_CAST(op, glsl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroup" #glsl_op, type, type); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroupInclusive" #glsl_op, type, type); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroupExclusive" #glsl_op, type, type); \ + else if (operation == GroupOperationClusteredReduce) \ + emit_binary_func_op_cast_clustered(result_type, id, ops[4], ops[5], "subgroupClustered" #glsl_op, type); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + GLSL_GROUP_OP(FAdd, Add) GLSL_GROUP_OP(FMul, Mul) GLSL_GROUP_OP(FMin, Min) GLSL_GROUP_OP(FMax, Max) GLSL_GROUP_OP(IAdd, Add) GLSL_GROUP_OP(IMul, Mul) - GLSL_GROUP_OP(SMin, Min) - GLSL_GROUP_OP(SMax, Max) - GLSL_GROUP_OP(UMin, Min) - GLSL_GROUP_OP(UMax, Max) + GLSL_GROUP_OP_CAST(SMin, Min, int_type) + GLSL_GROUP_OP_CAST(SMax, Max, int_type) + GLSL_GROUP_OP_CAST(UMin, Min, uint_type) + GLSL_GROUP_OP_CAST(UMax, Max, uint_type) GLSL_GROUP_OP(BitwiseAnd, And) GLSL_GROUP_OP(BitwiseOr, Or) GLSL_GROUP_OP(BitwiseXor, Xor) #undef GLSL_GROUP_OP +#undef GLSL_GROUP_OP_CAST // clang-format on case OpGroupNonUniformQuadSwap: @@ -6310,6 +6713,8 @@ string CompilerGLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &i // And finally, some even more special purpose casts. if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UInt && in_type.vecsize == 2) return "packUint2x32"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::UInt64 && out_type.vecsize == 2) + return "unpackUint2x32"; else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) return "unpackFloat2x16"; else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2) @@ -7592,7 +7997,16 @@ void CompilerGLSL::flush_variable_declaration(uint32_t id) auto *var = maybe_get(id); if (var && var->deferred_declaration) { - statement(variable_decl_function_local(*var), ";"); + string initializer; + if (options.force_zero_initialized_variables && + (var->storage == StorageClassFunction || var->storage == StorageClassGeneric || + var->storage == StorageClassPrivate) && + !var->initializer && type_can_zero_initialize(get_variable_data_type(*var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(*var))); + } + + statement(variable_decl_function_local(*var), initializer, ";"); var->deferred_declaration = false; } if (var) @@ -8163,6 +8577,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (expr.expression_dependencies.empty()) forwarded_temporaries.erase(ops[1]); + if (has_decoration(ops[1], DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(ops[1]); + break; } @@ -8170,9 +8587,6 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto *var = maybe_get(ops[0]); - if (has_decoration(ops[0], DecorationNonUniformEXT)) - propagate_nonuniform_qualifier(ops[0]); - if (var && var->statically_assigned) var->static_expression = ops[1]; else if (var && var->loop_variable && !var->loop_variable_enable) @@ -8378,7 +8792,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // This path cannot be used for arithmetic. if (backend.use_typed_initializer_list && out_type.basetype == SPIRType::Struct && out_type.array.empty()) constructor_op += type_to_glsl_constructor(get(result_type)); - else if (backend.use_typed_initializer_list && !out_type.array.empty()) + else if (backend.use_typed_initializer_list && backend.array_is_value_type && !out_type.array.empty()) { // MSL path. Array constructor is baked into type here, do not use _constructor variant. constructor_op += type_to_glsl_constructor(get(result_type)) + "("; @@ -8467,8 +8881,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (composite_type.basetype == SPIRType::Struct || !composite_type.array.empty()) allow_base_expression = false; - // Packed expressions cannot be split up. - if (has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypePacked)) + // Packed expressions or physical ID mapped expressions cannot be split up. + if (has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypePacked) || + has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypeID)) allow_base_expression = false; // Cannot use base expression for row-major matrix row-extraction since we need to interleave access pattern @@ -9179,8 +9594,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) uint32_t id = ops[1]; uint32_t arg = ops[2]; - auto op = bitcast_glsl_op(get(result_type), expression_type(arg)); - emit_unary_func_op(result_type, id, arg, op.c_str()); + if (!emit_complex_bitcast(result_type, id, arg)) + { + auto op = bitcast_glsl_op(get(result_type), expression_type(arg)); + emit_unary_func_op(result_type, id, arg, op.c_str()); + } break; } @@ -9681,7 +10099,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) } else if (type.image.dim == DimSubpassData) { - if (options.vulkan_semantics) + if (var && subpass_input_is_framebuffer_fetch(var->self)) + { + imgexpr = to_expression(var->self); + } + else if (options.vulkan_semantics) { // With Vulkan semantics, use the proper Vulkan GLSL construct. if (type.image.ms) @@ -9777,7 +10199,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) target_coord_type.basetype = SPIRType::Int; coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr); - auto &e = set(id, join(to_expression(ops[2]), ", ", coord_expr), result_type, true); + auto expr = join(to_expression(ops[2]), ", ", coord_expr); + if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT)) + convert_non_uniform_expression(expression_type(ops[2]), expr); + + auto &e = set(id, expr, result_type, true); // When using the pointer, we need to know which variable it is actually loaded from. auto *var = maybe_get_backing_variable(ops[2]); @@ -10750,6 +11176,37 @@ string CompilerGLSL::to_initializer_expression(const SPIRVariable &var) return to_expression(var.initializer); } +string CompilerGLSL::to_zero_initialized_expression(uint32_t type_id) +{ +#ifndef NDEBUG + auto &type = get(type_id); + assert(type.storage == StorageClassPrivate || type.storage == StorageClassFunction || + type.storage == StorageClassGeneric); +#endif + uint32_t id = ir.increase_bound_by(1); + ir.make_constant_null(id, type_id, false); + return constant_expression(get(id)); +} + +bool CompilerGLSL::type_can_zero_initialize(const SPIRType &type) const +{ + if (type.pointer) + return false; + + if (!type.array.empty() && options.flatten_multidimensional_arrays) + return false; + + for (auto &literal : type.array_size_literal) + if (!literal) + return false; + + for (auto &memb : type.member_types) + if (!type_can_zero_initialize(get(memb))) + return false; + + return true; +} + string CompilerGLSL::variable_decl(const SPIRVariable &variable) { // Ignore the pointer type since GLSL doesn't have pointers. @@ -10765,13 +11222,18 @@ string CompilerGLSL::variable_decl(const SPIRVariable &variable) uint32_t expr = variable.static_expression; if (ir.ids[expr].get_type() != TypeUndef) res += join(" = ", to_expression(variable.static_expression)); + else if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable))); } else if (variable.initializer) { uint32_t expr = variable.initializer; if (ir.ids[expr].get_type() != TypeUndef) res += join(" = ", to_initializer_expression(variable)); + else if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable))); } + return res; } @@ -10921,6 +11383,13 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id) if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics) return res + "subpassInput" + (type.image.ms ? "MS" : ""); + else if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + subpass_input_is_framebuffer_fetch(id)) + { + SPIRType sampled_type = get(type.image.type); + sampled_type.vecsize = 4; + return type_to_glsl(sampled_type); + } // If we're emulating subpassInput with samplers, force sampler2D // so we don't have to specify format. @@ -11052,7 +11521,7 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) // this distinction into the type system. return comparison_ids.count(id) ? "samplerShadow" : "sampler"; - case SPIRType::AccelerationStructureNV: + case SPIRType::AccelerationStructure: return "accelerationStructureNV"; case SPIRType::Void: @@ -11443,6 +11912,14 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) current_function = &func; auto &entry_block = get(func.entry_block); + sort(begin(func.constant_arrays_needed_on_stack), end(func.constant_arrays_needed_on_stack)); + for (auto &array : func.constant_arrays_needed_on_stack) + { + auto &c = get(array); + auto &type = get(c.constant_type); + statement(variable_decl(type, join("_", array, "_array_copy")), " = ", constant_expression(c), ";"); + } + for (auto &v : func.local_variables) { auto &var = get(v); @@ -11466,10 +11943,19 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) // If we don't declare the variable when it is assigned we're forced to go through a helper function // which copies elements one by one. add_local_variable_name(var.self); - auto &dominated = entry_block.dominated_variables; - if (find(begin(dominated), end(dominated), var.self) == end(dominated)) - entry_block.dominated_variables.push_back(var.self); - var.deferred_declaration = true; + + if (var.initializer) + { + statement(variable_decl(var), ";"); + var.deferred_declaration = false; + } + else + { + auto &dominated = entry_block.dominated_variables; + if (find(begin(dominated), end(dominated), var.self) == end(dominated)) + entry_block.dominated_variables.push_back(var.self); + var.deferred_declaration = true; + } } else if (var.storage == StorageClassFunction && var.remapped_variable && var.static_expression) { @@ -12145,7 +12631,13 @@ void CompilerGLSL::emit_hoisted_temporaries(SmallVector> &tempo add_local_variable_name(tmp.second); auto &flags = ir.meta[tmp.second].decoration.decoration_flags; auto &type = get(tmp.first); - statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(tmp.second)), ";"); + + // Not all targets support pointer literals, so don't bother with that case. + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(tmp.first)); + + statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(tmp.second)), initializer, ";"); hoisted_temporaries.insert(tmp.second); forced_temporaries.insert(tmp.second); @@ -13189,3 +13681,74 @@ void CompilerGLSL::emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, emit_store_statement(lhs_id, rhs_id); } } + +bool CompilerGLSL::subpass_input_is_framebuffer_fetch(uint32_t id) const +{ + if (!has_decoration(id, DecorationInputAttachmentIndex)) + return false; + + uint32_t input_attachment_index = get_decoration(id, DecorationInputAttachmentIndex); + for (auto &remap : subpass_to_framebuffer_fetch_attachment) + if (remap.first == input_attachment_index) + return true; + + return false; +} + +const SPIRVariable *CompilerGLSL::find_subpass_input_by_attachment_index(uint32_t index) const +{ + const SPIRVariable *ret = nullptr; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (has_decoration(var.self, DecorationInputAttachmentIndex) && + get_decoration(var.self, DecorationInputAttachmentIndex) == index) + { + ret = &var; + } + }); + return ret; +} + +const SPIRVariable *CompilerGLSL::find_color_output_by_location(uint32_t location) const +{ + const SPIRVariable *ret = nullptr; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (var.storage == StorageClassOutput && get_decoration(var.self, DecorationLocation) == location) + ret = &var; + }); + return ret; +} + +void CompilerGLSL::emit_inout_fragment_outputs_copy_to_subpass_inputs() +{ + for (auto &remap : subpass_to_framebuffer_fetch_attachment) + { + auto *subpass_var = find_subpass_input_by_attachment_index(remap.first); + auto *output_var = find_color_output_by_location(remap.second); + if (!subpass_var) + continue; + if (!output_var) + SPIRV_CROSS_THROW("Need to declare the corresponding fragment output variable to be able to read from it."); + if (is_array(get(output_var->basetype))) + SPIRV_CROSS_THROW("Cannot use GL_EXT_shader_framebuffer_fetch with arrays of color outputs."); + + auto &func = get(get_entry_point().self); + func.fixup_hooks_in.push_back([=]() { + if (is_legacy()) + { + statement(to_expression(subpass_var->self), " = ", "gl_LastFragData[", + get_decoration(output_var->self, DecorationLocation), "];"); + } + else + { + uint32_t num_rt_components = this->get(output_var->basetype).vecsize; + statement(to_expression(subpass_var->self), vector_swizzle(num_rt_components, 0), " = ", + to_expression(output_var->self), ";"); + } + }); + } +} + +bool CompilerGLSL::variable_is_depth_or_compare(VariableID id) const +{ + return image_is_comparison(get(get(id).basetype), id); +} diff --git a/src/libraries/spirv_cross/spirv_glsl.hpp b/src/libraries/spirv_cross/spirv_glsl.hpp index 32d8f42ca..1eafc2cea 100644 --- a/src/libraries/spirv_cross/spirv_glsl.hpp +++ b/src/libraries/spirv_cross/spirv_glsl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 Arm Limited + * Copyright 2015-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,6 +108,18 @@ public: // May not correspond exactly to original source, but should be a good approximation. bool emit_line_directives = false; + // In cases where readonly/writeonly decoration are not used at all, + // we try to deduce which qualifier(s) we should actually used, since actually emitting + // read-write decoration is very rare, and older glslang/HLSL compilers tend to just emit readwrite as a matter of fact. + // The default (true) is to enable automatic deduction for these cases, but if you trust the decorations set + // by the SPIR-V, it's recommended to set this to false. + bool enable_storage_image_qualifier_deduction = true; + + // On some targets (WebGPU), uninitialized variables are banned. + // If this is enabled, all variables (temporaries, Private, Function) + // which would otherwise be uninitialized will now be initialized to 0 instead. + bool force_zero_initialized_variables = false; + enum Precision { DontCare, @@ -149,6 +161,10 @@ public: remap_pls_variables(); } + // Redirect a subpassInput reading from input_attachment_index to instead load its value from + // the color attachment at location = color_location. Requires ESSL. + void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location); + explicit CompilerGLSL(std::vector spirv_) : Compiler(std::move(spirv_)) { @@ -212,6 +228,14 @@ public: // The name of the uniform array will be the same as the interface block name. void flatten_buffer_block(VariableID id); + // After compilation, query if a variable ID was used as a depth resource. + // This is meaningful for MSL since descriptor types depend on this knowledge. + // Cases which return true: + // - Images which are declared with depth = 1 image type. + // - Samplers which are statically used at least once with Dref opcodes. + // - Images which are statically used at least once with Dref opcodes. + bool variable_is_depth_or_compare(VariableID id) const; + protected: void reset(); void emit_function(SPIRFunction &func, const Bitset &return_flags); @@ -431,6 +455,7 @@ protected: void emit_buffer_block_legacy(const SPIRVariable &var); void emit_buffer_block_flattened(const SPIRVariable &type); void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model); + bool should_force_emit_builtin_block(spv::StorageClass storage); void emit_push_constant_block_vulkan(const SPIRVariable &var); void emit_push_constant_block_glsl(const SPIRVariable &var); void emit_interface_block(const SPIRVariable &type); @@ -467,6 +492,8 @@ protected: SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type); void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type); + void emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type); void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op, SPIRType::BaseType input_type); void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, @@ -487,6 +514,8 @@ protected: SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type, uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type); + virtual bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0); + std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value, uint32_t false_value); @@ -562,6 +591,8 @@ protected: spv::StorageClass rhs_storage); virtual void emit_block_hints(const SPIRBlock &block); virtual std::string to_initializer_expression(const SPIRVariable &var); + virtual std::string to_zero_initialized_expression(uint32_t type_id); + bool type_can_zero_initialize(const SPIRType &type) const; bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t *failed_index = nullptr, uint32_t start_offset = 0, @@ -588,6 +619,7 @@ protected: bool check_atomic_image(uint32_t id); virtual void replace_illegal_names(); + void replace_illegal_names(const std::unordered_set &keywords); virtual void emit_entry_point_declarations(); void replace_fragment_output(SPIRVariable &var); @@ -652,6 +684,14 @@ protected: void emit_pls(); void remap_pls_variables(); + // GL_EXT_shader_framebuffer_fetch support. + std::vector> subpass_to_framebuffer_fetch_attachment; + std::unordered_set inout_color_attachments; + bool subpass_input_is_framebuffer_fetch(uint32_t id) const; + void emit_inout_fragment_outputs_copy_to_subpass_inputs(); + const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const; + const SPIRVariable *find_color_output_by_location(uint32_t location) const; + // A variant which takes two sets of name. The secondary is only used to verify there are no collisions, // but the set is not updated when we have found a new name. // Used primarily when adding block interface names. diff --git a/src/libraries/spirv_cross/spirv_hlsl.cpp b/src/libraries/spirv_cross/spirv_hlsl.cpp index 068483211..73e46bc2c 100644 --- a/src/libraries/spirv_cross/spirv_hlsl.cpp +++ b/src/libraries/spirv_cross/spirv_hlsl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 Robert Konrad + * Copyright 2016-2020 Robert Konrad * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -210,6 +210,8 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id) bool typed_load = false; uint32_t components = 4; + bool force_image_srv = hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(id, DecorationNonWritable); + switch (type.image.dim) { case Dim1D: @@ -239,7 +241,14 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id) if (interlocked_resources.count(id)) return join("RasterizerOrderedBuffer<", image_format_to_type(type.image.format, imagetype.basetype), ">"); - return join("RWBuffer<", image_format_to_type(type.image.format, imagetype.basetype), ">"); + + typed_load = !force_image_srv && type.image.sampled == 2; + + const char *rw = force_image_srv ? "" : "RW"; + return join(rw, "Buffer<", + typed_load ? image_format_to_type(type.image.format, imagetype.basetype) : + join(type_to_glsl(imagetype), components), + ">"); } else SPIRV_CROSS_THROW("Sampler buffers must be either sampled or unsampled. Cannot deduce in runtime."); @@ -252,9 +261,14 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id) } const char *arrayed = type.image.arrayed ? "Array" : ""; const char *ms = type.image.ms ? "MS" : ""; - const char *rw = typed_load ? "RW" : ""; + const char *rw = typed_load && !force_image_srv ? "RW" : ""; + + if (force_image_srv) + typed_load = false; + if (typed_load && interlocked_resources.count(id)) rw = "RasterizerOrdered"; + return join(rw, "Texture", dim, ms, arrayed, "<", typed_load ? image_format_to_type(type.image.format, imagetype.basetype) : join(type_to_glsl(imagetype), components), @@ -387,8 +401,12 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id) case SPIRType::Double: return "double"; case SPIRType::Int64: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0."); return "int64_t"; case SPIRType::UInt64: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0."); return "uint64_t"; default: return "???"; @@ -1113,16 +1131,25 @@ void CompilerHLSL::replace_illegal_names() "line", "linear", "matrix", "point", "row_major", "sampler", }; - ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { - if (!is_hidden_variable(var)) - { - auto &m = ir.meta[var.self].decoration; - if (keywords.find(m.alias) != end(keywords)) - m.alias = join("_", m.alias); - } + CompilerGLSL::replace_illegal_names(keywords); + CompilerGLSL::replace_illegal_names(); +} + +void CompilerHLSL::declare_undefined_values() +{ + bool emitted = false; + ir.for_each_typed_id([&](uint32_t, const SPIRUndef &undef) { + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(this->get(undef.basetype))) + initializer = join(" = ", to_zero_initialized_expression(undef.basetype)); + + statement("static ", variable_decl(this->get(undef.basetype), to_name(undef.self), undef.self), + initializer, ";"); + emitted = true; }); - CompilerGLSL::replace_illegal_names(); + if (emitted) + statement(""); } void CompilerHLSL::emit_resources() @@ -1358,7 +1385,15 @@ void CompilerHLSL::emit_resources() storage = "static"; break; } - statement(storage, " ", variable_decl(var), ";"); + + string initializer; + if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate && + !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var))); + } + statement(storage, " ", variable_decl(var), initializer, ";"); + emitted = true; } } @@ -1468,6 +1503,24 @@ void CompilerHLSL::emit_resources() statement(""); } + if (requires_uint2_packing) + { + statement("uint64_t SPIRV_Cross_packUint2x32(uint2 value)"); + begin_scope(); + statement("return (uint64_t(value.y) << 32) | uint64_t(value.x);"); + end_scope(); + statement(""); + + statement("uint2 SPIRV_Cross_unpackUint2x32(uint64_t value)"); + begin_scope(); + statement("uint2 Unpacked;"); + statement("Unpacked.x = uint(value & 0xffffffff);"); + statement("Unpacked.y = uint(value >> 32);"); + statement("return Unpacked;"); + end_scope(); + statement(""); + } + if (requires_explicit_fp16_packing) { // HLSL does not pack into a single word sadly :( @@ -1853,8 +1906,8 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var) if (is_uav) { Bitset flags = ir.get_buffer_block_flags(var); - bool is_readonly = flags.get(DecorationNonWritable); - bool is_coherent = flags.get(DecorationCoherent); + bool is_readonly = flags.get(DecorationNonWritable) && !hlsl_options.force_storage_buffer_as_uav; + bool is_coherent = flags.get(DecorationCoherent) && !is_readonly; bool is_interlocked = interlocked_resources.count(var.self) > 0; const char *type_name = "ByteAddressBuffer "; if (!is_readonly) @@ -2934,16 +2987,15 @@ void CompilerHLSL::emit_texture_op(const Instruction &i) string CompilerHLSL::to_resource_binding(const SPIRVariable &var) { - // TODO: Basic implementation, might need special consideration for RW/RO structured buffers, - // RW/RO images, and so on. + const auto &type = get(var.basetype); - if (!has_decoration(var.self, DecorationBinding)) + // We can remap push constant blocks, even if they don't have any binding decoration. + if (type.storage != StorageClassPushConstant && !has_decoration(var.self, DecorationBinding)) return ""; - const auto &type = get(var.basetype); char space = '\0'; - HLSLBindingFlags resource_flags = 0; + HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT; switch (type.basetype) { @@ -2955,8 +3007,16 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) case SPIRType::Image: if (type.image.sampled == 2 && type.image.dim != DimSubpassData) { - space = 'u'; // UAV - resource_flags = HLSL_BINDING_AUTO_UAV_BIT; + if (has_decoration(var.self, DecorationNonWritable) && hlsl_options.nonwritable_uav_texture_as_srv) + { + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + } + else + { + space = 'u'; // UAV + resource_flags = HLSL_BINDING_AUTO_UAV_BIT; + } } else { @@ -2978,7 +3038,7 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) if (has_decoration(type.self, DecorationBufferBlock)) { Bitset flags = ir.get_buffer_block_flags(var); - bool is_readonly = flags.get(DecorationNonWritable); + bool is_readonly = flags.get(DecorationNonWritable) && !hlsl_options.force_storage_buffer_as_uav; space = is_readonly ? 't' : 'u'; // UAV resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT; } @@ -2997,7 +3057,7 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) { // UAV or SRV depending on readonly flag. Bitset flags = ir.get_buffer_block_flags(var); - bool is_readonly = flags.get(DecorationNonWritable); + bool is_readonly = flags.get(DecorationNonWritable) && !hlsl_options.force_storage_buffer_as_uav; space = is_readonly ? 't' : 'u'; resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT; } @@ -3011,8 +3071,16 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) if (!space) return ""; - return to_resource_register(resource_flags, space, get_decoration(var.self, DecorationBinding), - get_decoration(var.self, DecorationDescriptorSet)); + uint32_t desc_set = + resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantDescriptorSet : 0u; + uint32_t binding = resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantBinding : 0u; + + if (has_decoration(var.self, DecorationBinding)) + binding = get_decoration(var.self, DecorationBinding); + if (has_decoration(var.self, DecorationDescriptorSet)) + desc_set = get_decoration(var.self, DecorationDescriptorSet); + + return to_resource_register(resource_flags, space, binding, desc_set); } string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) @@ -3025,10 +3093,54 @@ string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) get_decoration(var.self, DecorationDescriptorSet)); } -string CompilerHLSL::to_resource_register(uint32_t flags, char space, uint32_t binding, uint32_t space_set) +void CompilerHLSL::remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding) { - if ((flags & resource_binding_flags) == 0) + auto itr = resource_bindings.find({ get_execution_model(), desc_set, binding }); + if (itr != end(resource_bindings)) { + auto &remap = itr->second; + remap.second = true; + + switch (type) + { + case HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT: + case HLSL_BINDING_AUTO_CBV_BIT: + desc_set = remap.first.cbv.register_space; + binding = remap.first.cbv.register_binding; + break; + + case HLSL_BINDING_AUTO_SRV_BIT: + desc_set = remap.first.srv.register_space; + binding = remap.first.srv.register_binding; + break; + + case HLSL_BINDING_AUTO_SAMPLER_BIT: + desc_set = remap.first.sampler.register_space; + binding = remap.first.sampler.register_binding; + break; + + case HLSL_BINDING_AUTO_UAV_BIT: + desc_set = remap.first.uav.register_space; + binding = remap.first.uav.register_binding; + break; + + default: + break; + } + } +} + +string CompilerHLSL::to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t space_set) +{ + if ((flag & resource_binding_flags) == 0) + { + remap_hlsl_resource_binding(flag, space_set, binding); + + // The push constant block did not have a binding, and there were no remap for it, + // so, declare without register binding. + if (flag == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT && space_set == ResourceBindingPushConstantDescriptorSet) + return ""; + if (hlsl_options.shader_model >= 51) return join(" : register(", space, binding, ", space", space_set, ")"); else @@ -3104,6 +3216,11 @@ void CompilerHLSL::emit_uniform(const SPIRVariable &var) emit_legacy_uniform(var); } +bool CompilerHLSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + return false; +} + string CompilerHLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) { if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Int) @@ -3955,6 +4072,9 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction) inherit_expression_dependencies(ops[1], ops[i]); add_implied_read_expression(e, ops[i]); } + + if (has_decoration(ops[1], DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(ops[1]); } else { @@ -4079,6 +4199,11 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i) return join(expr, " * ", to_expression(ops[4])); }; + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + #define make_inclusive_BitAnd(expr) "" #define make_inclusive_BitOr(expr) "" #define make_inclusive_BitXor(expr) "" @@ -4187,20 +4312,34 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } + +#define HLSL_GROUP_OP_CAST(op, hlsl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "WaveActive" #hlsl_op, type, type); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + HLSL_GROUP_OP(FAdd, Sum, true) HLSL_GROUP_OP(FMul, Product, true) HLSL_GROUP_OP(FMin, Min, false) HLSL_GROUP_OP(FMax, Max, false) HLSL_GROUP_OP(IAdd, Sum, true) HLSL_GROUP_OP(IMul, Product, true) - HLSL_GROUP_OP(SMin, Min, false) - HLSL_GROUP_OP(SMax, Max, false) - HLSL_GROUP_OP(UMin, Min, false) - HLSL_GROUP_OP(UMax, Max, false) + HLSL_GROUP_OP_CAST(SMin, Min, int_type) + HLSL_GROUP_OP_CAST(SMax, Max, int_type) + HLSL_GROUP_OP_CAST(UMin, Min, uint_type) + HLSL_GROUP_OP_CAST(UMax, Max, uint_type) HLSL_GROUP_OP(BitwiseAnd, BitAnd, false) HLSL_GROUP_OP(BitwiseOr, BitOr, false) HLSL_GROUP_OP(BitwiseXor, BitXor, false) + #undef HLSL_GROUP_OP +#undef HLSL_GROUP_OP_CAST // clang-format on case OpGroupNonUniformQuadSwap: @@ -4260,6 +4399,27 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) emit_access_chain(instruction); break; } + case OpBitcast: + { + auto bitcast_type = get_bitcast_type(ops[0], ops[2]); + if (bitcast_type == CompilerHLSL::TypeNormal) + CompilerGLSL::emit_instruction(instruction); + else + { + if (!requires_uint2_packing) + { + requires_uint2_packing = true; + force_recompile(); + } + + if (bitcast_type == CompilerHLSL::TypePackUint2x32) + emit_unary_func_op(ops[0], ops[1], ops[2], "SPIRV_Cross_packUint2x32"); + else + emit_unary_func_op(ops[0], ops[1], ops[2], "SPIRV_Cross_unpackUint2x32"); + } + + break; + } case OpStore: { @@ -4723,7 +4883,12 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]"); // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4", // except that the underlying type changes how the data is interpreted. - if (var && !subpass_data) + + bool force_srv = + hlsl_options.nonwritable_uav_texture_as_srv && var && has_decoration(var->self, DecorationNonWritable); + pure = force_srv; + + if (var && !subpass_data && !force_srv) imgexpr = remap_swizzle(get(result_type), image_format_to_components(get(var->basetype).image.format), imgexpr); } @@ -4774,8 +4939,13 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) { uint32_t result_type = ops[0]; uint32_t id = ops[1]; - auto &e = - set(id, join(to_expression(ops[2]), "[", to_expression(ops[3]), "]"), result_type, true); + + auto expr = to_expression(ops[2]); + if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT)) + convert_non_uniform_expression(expression_type(ops[2]), expr); + expr += join("[", to_expression(ops[3]), "]"); + + auto &e = set(id, expr, result_type, true); // When using the pointer, we need to know which variable it is actually loaded from. auto *var = maybe_get_backing_variable(ops[2]); @@ -5215,3 +5385,31 @@ string CompilerHLSL::get_unique_identifier() { return join("_", unique_identifier_count++, "ident"); } + +void CompilerHLSL::add_hlsl_resource_binding(const HLSLResourceBinding &binding) +{ + StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding }; + resource_bindings[tuple] = { binding, false }; +} + +bool CompilerHLSL::is_hlsl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const +{ + StageSetBinding tuple = { model, desc_set, binding }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) && itr->second.second; +} + +CompilerHLSL::BitcastType CompilerHLSL::get_bitcast_type(uint32_t result_type, uint32_t op0) +{ + auto &rslt_type = get(result_type); + auto &expr_type = expression_type(op0); + + if (rslt_type.basetype == SPIRType::BaseType::UInt64 && expr_type.basetype == SPIRType::BaseType::UInt && + expr_type.vecsize == 2) + return BitcastType::TypePackUint2x32; + else if (rslt_type.basetype == SPIRType::BaseType::UInt && rslt_type.vecsize == 2 && + expr_type.basetype == SPIRType::BaseType::UInt64) + return BitcastType::TypeUnpackUint64; + + return BitcastType::TypeNormal; +} diff --git a/src/libraries/spirv_cross/spirv_hlsl.hpp b/src/libraries/spirv_cross/spirv_hlsl.hpp index 5663ec2a8..e98c6443a 100644 --- a/src/libraries/spirv_cross/spirv_hlsl.hpp +++ b/src/libraries/spirv_cross/spirv_hlsl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 Robert Konrad + * Copyright 2016-2020 Robert Konrad * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,8 @@ struct RootConstants // For finer control, decorations may be removed from specific resources instead with unset_decoration(). enum HLSLBindingFlagBits { + HLSL_BINDING_AUTO_NONE_BIT = 0, + // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. @@ -67,6 +69,28 @@ enum HLSLBindingFlagBits }; using HLSLBindingFlags = uint32_t; +// By matching stage, desc_set and binding for a SPIR-V resource, +// register bindings are set based on whether the HLSL resource is a +// CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple +// resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively. +// On SM 5.0 and lower, register_space is ignored. +// +// To remap a push constant block which does not have any desc_set/binding associated with it, +// use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding. +// For deeper control of push constants, set_root_constant_layouts() can be used instead. +struct HLSLResourceBinding +{ + spv::ExecutionModel stage = spv::ExecutionModelMax; + uint32_t desc_set = 0; + uint32_t binding = 0; + + struct Binding + { + uint32_t register_space = 0; + uint32_t register_binding = 0; + } cbv, uav, srv, sampler; +}; + class CompilerHLSL : public CompilerGLSL { public: @@ -85,6 +109,15 @@ public: // Set to false if you know you will never use base instance or base vertex // functionality as it might remove an internal cbuffer. bool support_nonzero_base_vertex_base_instance = false; + + // Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used. + // By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead. + bool force_storage_buffer_as_uav = false; + + // Forces any storage image type marked as NonWritable to be considered an SRV instead. + // For this to work with function call parameters, NonWritable must be considered to be part of the type system + // so that NonWritable image arguments are also translated to Texture rather than RWTexture. + bool nonwritable_uav_texture_as_srv = false; }; explicit CompilerHLSL(std::vector spirv_) @@ -145,6 +178,14 @@ public: // Controls how resource bindings are declared in the output HLSL. void set_resource_binding_flags(HLSLBindingFlags flags); + // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding + // to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_hlsl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the HLSL code. + void add_hlsl_resource_binding(const HLSLResourceBinding &resource); + bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; + private: std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; std::string image_type_hlsl(const SPIRType &type, uint32_t id); @@ -154,6 +195,7 @@ private: void emit_hlsl_entry_point(); void emit_header() override; void emit_resources(); + void declare_undefined_values() override; void emit_interface_block_globally(const SPIRVariable &type); void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set &active_locations); void emit_builtin_inputs_in_struct(); @@ -174,11 +216,12 @@ private: std::string layout_for_member(const SPIRType &type, uint32_t index) override; std::string to_interpolation_qualifiers(const Bitset &flags) override; std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override; std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; std::string to_sampler_expression(uint32_t id); std::string to_resource_binding(const SPIRVariable &var); std::string to_resource_binding_sampler(const SPIRVariable &var); - std::string to_resource_register(HLSLBindingFlags flags, char space, uint32_t binding, uint32_t set); + std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set); void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; void emit_access_chain(const Instruction &instruction); void emit_load(const Instruction &instruction); @@ -207,6 +250,7 @@ private: // TODO: Refactor this to be more similar to MSL, maybe have some common system in place? bool requires_op_fmod = false; bool requires_fp16_packing = false; + bool requires_uint2_packing = false; bool requires_explicit_fp16_packing = false; bool requires_unorm8_packing = false; bool requires_snorm8_packing = false; @@ -246,6 +290,15 @@ private: QueryTypeCount = 3 }; + enum BitcastType + { + TypeNormal, + TypePackUint2x32, + TypeUnpackUint64 + }; + + BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0); + void emit_builtin_variables(); bool require_output = false; bool require_input = false; @@ -267,6 +320,9 @@ private: std::string get_unique_identifier(); uint32_t unique_identifier_count = 0; + + std::unordered_map, InternalHasher> resource_bindings; + void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding); }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/src/libraries/spirv_cross/spirv_msl.cpp b/src/libraries/spirv_cross/spirv_msl.cpp index 0b5e9fc5c..597cf89cd 100644 --- a/src/libraries/spirv_cross/spirv_msl.cpp +++ b/src/libraries/spirv_cross/spirv_msl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The Brenwill Workshop Ltd. + * Copyright 2016-2020 The Brenwill Workshop Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,6 +68,12 @@ void CompilerMSL::add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32 buffers_requiring_dynamic_offset[pair] = { index, 0 }; } +void CompilerMSL::add_inline_uniform_block(uint32_t desc_set, uint32_t binding) +{ + SetBindingPair pair = { desc_set, binding }; + inline_uniform_blocks.insert(pair); +} + void CompilerMSL::add_discrete_descriptor_set(uint32_t desc_set) { if (desc_set < kMaxArgumentBuffers) @@ -90,7 +96,7 @@ bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location) return vtx_attrs_in_use.count(location) != 0; } -bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) +bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const { StageSetBinding tuple = { model, desc_set, binding }; auto itr = resource_bindings.find(tuple); @@ -168,6 +174,7 @@ void CompilerMSL::build_implicit_builtins() if (need_subpass_input && (!msl_options.is_ios() || !msl_options.ios_use_framebuffer_fetch_subpasses) && builtin == BuiltInFragCoord) { + mark_implicit_builtin(StorageClassInput, BuiltInFragCoord, var.self); builtin_frag_coord_id = var.self; has_frag_coord = true; } @@ -175,6 +182,7 @@ void CompilerMSL::build_implicit_builtins() if (need_sample_pos && builtin == BuiltInSampleId) { builtin_sample_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var.self); has_sample_id = true; } @@ -184,18 +192,22 @@ void CompilerMSL::build_implicit_builtins() { case BuiltInVertexIndex: builtin_vertex_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInVertexIndex, var.self); has_vertex_idx = true; break; case BuiltInBaseVertex: builtin_base_vertex_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseVertex, var.self); has_base_vertex = true; break; case BuiltInInstanceIndex: builtin_instance_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var.self); has_instance_idx = true; break; case BuiltInBaseInstance: builtin_base_instance_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var.self); has_base_instance = true; break; default: @@ -209,10 +221,12 @@ void CompilerMSL::build_implicit_builtins() { case BuiltInInvocationId: builtin_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self); has_invocation_id = true; break; case BuiltInPrimitiveId: builtin_primitive_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self); has_primitive_id = true; break; default: @@ -223,12 +237,14 @@ void CompilerMSL::build_implicit_builtins() if ((need_subgroup_mask || needs_subgroup_invocation_id) && builtin == BuiltInSubgroupLocalInvocationId) { builtin_subgroup_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupLocalInvocationId, var.self); has_subgroup_invocation_id = true; } if (need_subgroup_ge_mask && builtin == BuiltInSubgroupSize) { builtin_subgroup_size_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupSize, var.self); has_subgroup_size = true; } @@ -239,10 +255,12 @@ void CompilerMSL::build_implicit_builtins() case BuiltInInstanceIndex: // The view index here is derived from the instance index. builtin_instance_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var.self); has_instance_idx = true; break; case BuiltInViewIndex: builtin_view_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var.self); has_view_idx = true; break; default: @@ -639,7 +657,10 @@ void CompilerMSL::mark_implicit_builtin(StorageClass storage, BuiltIn builtin, u assert(active_builtins != nullptr); active_builtins->set(builtin); - get_entry_point().interface_variables.push_back(id); + + auto &var = get_entry_point().interface_variables; + if (find(begin(var), end(var), VariableID(id)) == end(var)) + var.push_back(id); } uint32_t CompilerMSL::build_constant_uint_array_pointer() @@ -884,7 +905,7 @@ void CompilerMSL::emit_entry_point_declarations() SPIRV_CROSS_THROW("Runtime arrays with dynamic offsets are not supported yet."); else { - use_builtin_array = true; + is_using_builtin_array = true; statement(get_argument_address_space(var), " ", type_to_glsl(type), "* ", to_restrict(var_id), name, type_to_array_glsl(type), " ="); @@ -915,7 +936,7 @@ void CompilerMSL::emit_entry_point_declarations() } end_scope_decl(); statement_no_indent(""); - use_builtin_array = false; + is_using_builtin_array = false; } } else @@ -944,6 +965,16 @@ void CompilerMSL::emit_entry_point_declarations() } // For some reason, without this, we end up emitting the arrays twice. buffer_arrays.clear(); + + // Emit disabled fragment outputs. + std::sort(disabled_frag_outputs.begin(), disabled_frag_outputs.end()); + for (uint32_t var_id : disabled_frag_outputs) + { + auto &var = get(var_id); + add_local_variable_name(var_id); + statement(variable_decl(var), ";"); + var.deferred_declaration = false; + } } string CompilerMSL::compile() @@ -973,15 +1004,17 @@ string CompilerMSL::compile() backend.native_row_major_matrix = false; backend.unsized_array_supported = false; backend.can_declare_arrays_inline = false; - backend.can_return_array = true; // <-- Allow Metal to use the array template backend.allow_truncated_access_chain = true; - backend.array_is_value_type = true; // <-- Allow Metal to use the array template to make arrays a value type backend.comparison_image_samples_scalar = true; backend.native_pointers = true; backend.nonuniform_qualifier = ""; backend.support_small_type_sampling_result = true; backend.supports_empty_struct = true; + // Allow Metal to use the array template unless we force it off. + backend.can_return_array = !msl_options.force_native_arrays; + backend.array_is_value_type = !msl_options.force_native_arrays; + capture_output_to_buffer = msl_options.capture_output_to_buffer; is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer; @@ -1003,6 +1036,9 @@ string CompilerMSL::compile() fixup_image_load_store_access(); set_enabled_interface_variables(get_active_interface_variables()); + if (msl_options.force_active_argument_buffer_resources) + activate_argument_buffer_resources(); + if (swizzle_buffer_id) active_interface_variables.insert(swizzle_buffer_id); if (buffer_size_buffer_id) @@ -1701,6 +1737,7 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction)); bool flatten_from_ib_var = false; + string flatten_from_ib_mbr_name; if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) { @@ -1708,11 +1745,16 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); ib_type.member_types.push_back(get_variable_data_type_id(var)); set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); - set_member_name(ib_type.self, clip_array_mbr_idx, builtin_to_glsl(BuiltInClipDistance, StorageClassOutput)); + + flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput); + set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name); // When we flatten, we flatten directly from the "out" struct, // not from a function variable. flatten_from_ib_var = true; + + if (!msl_options.enable_clip_distance_user_varying) + return; } else if (!meta.strip_array) { @@ -1817,7 +1859,8 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage ";"); } else if (flatten_from_ib_var) - statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", to_name(var.self), "[", i, "];"); + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i, + "];"); else statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];"); }); @@ -1902,6 +1945,7 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass usable_type = &get(usable_type->parent_type); bool flatten_from_ib_var = false; + string flatten_from_ib_mbr_name; if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) { @@ -1909,11 +1953,16 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); ib_type.member_types.push_back(mbr_type_id); set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); - set_member_name(ib_type.self, clip_array_mbr_idx, builtin_to_glsl(BuiltInClipDistance, StorageClassOutput)); + + flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput); + set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name); // When we flatten, we flatten directly from the "out" struct, // not from a function variable. flatten_from_ib_var = true; + + if (!msl_options.enable_clip_distance_user_varying) + return; } for (uint32_t i = 0; i < elem_cnt; i++) @@ -1983,8 +2032,8 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() { if (flatten_from_ib_var) { - statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", to_member_name(var_type, mbr_idx), - "[", i, "];"); + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i, + "];"); } else { @@ -2409,6 +2458,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) bool is_builtin = is_builtin_variable(var); auto bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn)); + uint32_t location = get_decoration(var_id, DecorationLocation); // These builtins are part of the stage in/out structs. bool is_interface_block_builtin = @@ -2434,6 +2484,21 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) if (bi_type == BuiltInClipDistance) hidden = false; + // It's not enough to simply avoid marking fragment outputs if the pipeline won't + // accept them. We can't put them in the struct at all, or otherwise the compiler + // complains that the outputs weren't explicitly marked. + if (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput && !patch && + ((is_builtin && ((bi_type == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) || + (bi_type == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin))) || + (!is_builtin && !(msl_options.enable_frag_output_mask & (1 << location))))) + { + hidden = true; + disabled_frag_outputs.push_back(var_id); + // If a builtin, force it to have the proper name. + if (is_builtin) + set_name(var_id, builtin_to_glsl(bi_type, StorageClassFunction)); + } + // Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments. if (is_active && (bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV)) { @@ -2463,7 +2528,6 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) SPIRV_CROSS_THROW("Component decoration is not supported in tessellation shaders."); else if (pack_components) { - uint32_t location = get_decoration(var_id, DecorationLocation); auto &location_meta = meta.location_meta[location]; location_meta.num_components = std::max(location_meta.num_components, component + type.vecsize); } @@ -3022,10 +3086,20 @@ bool CompilerMSL::validate_member_packing_rules_msl(const SPIRType &type, uint32 if (!mbr_type.array.empty()) { // If we have an array type, array stride must match exactly with SPIR-V. - uint32_t spirv_array_stride = type_struct_member_array_stride(type, index); - uint32_t msl_array_stride = get_declared_struct_member_array_stride_msl(type, index); - if (spirv_array_stride != msl_array_stride) - return false; + + // An exception to this requirement is if we have one array element and a packed decoration. + // This comes from DX scalar layout workaround. + // If app tries to be cheeky and access the member out of bounds, this will not work, but this is the best we can do. + bool relax_array_stride = has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypePacked) && + mbr_type.array.back() == 1 && mbr_type.array_size_literal.back(); + + if (!relax_array_stride) + { + uint32_t spirv_array_stride = type_struct_member_array_stride(type, index); + uint32_t msl_array_stride = get_declared_struct_member_array_stride_msl(type, index); + if (spirv_array_stride != msl_array_stride) + return false; + } } if (is_matrix(mbr_type)) @@ -3137,6 +3211,77 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in else unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); } + else + SPIRV_CROSS_THROW("Found a buffer packing case which we cannot represent in MSL."); + + // Try validating again, now with physical type remapping. + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We might have a particular odd scalar layout case where the last element of an array + // does not take up as much space as the ArrayStride or MatrixStride. This can happen with DX cbuffers. + // The "proper" workaround for this is extremely painful and essentially impossible in the edge case of float3[], + // so we hack around it by declaring the offending array or matrix with one less array size/col/row, + // and rely on padding to get the correct value. We will technically access arrays out of bounds into the padding region, + // but it should spill over gracefully without too much trouble. We rely on behavior like this for unsized arrays anyways. + + // E.g. we might observe a physical layout of: + // { float2 a[2]; float b; } in cbuffer layout where ArrayStride of a is 16, but offset of b is 24, packed right after a[1] ... + uint32_t type_id = get_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID); + auto &type = get(type_id); + + // Modify the physical type in-place. This is safe since each physical type workaround is a copy. + if (is_array(type)) + { + if (type.array.back() > 1) + { + if (!type.array_size_literal.back()) + SPIRV_CROSS_THROW("Cannot apply scalar layout workaround with spec constant array size."); + type.array.back() -= 1; + } + else + { + // We have an array of size 1, so we cannot decrement that. Our only option now is to + // force a packed layout instead, and drop the physical type remap since ArrayStride is meaningless now. + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + } + else if (is_matrix(type)) + { + bool row_major = has_member_decoration(ib_type.self, index, DecorationRowMajor); + if (!row_major) + { + // Slice off one column. If we only have 2 columns, this might turn the matrix into a vector with one array element instead. + if (type.columns > 2) + { + type.columns--; + } + else if (type.columns == 2) + { + type.columns = 1; + assert(type.array.empty()); + type.array.push_back(1); + type.array_size_literal.push_back(true); + } + } + else + { + // Slice off one row. If we only have 2 rows, this might turn the matrix into a vector with one array element instead. + if (type.vecsize > 2) + { + type.vecsize--; + } + else if (type.vecsize == 2) + { + type.vecsize = type.columns; + type.columns = 1; + assert(type.array.empty()); + type.array.push_back(1); + type.array_size_literal.push_back(true); + } + } + } // This better validate now, or we must fail gracefully. if (!validate_member_packing_rules_msl(ib_type, index)) @@ -6691,29 +6836,6 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin bar_stmt += "mem_flags::mem_none"; } - if (msl_options.is_ios() && (msl_options.supports_msl_version(2) && !msl_options.supports_msl_version(2, 1))) - { - bar_stmt += ", "; - - switch (mem_scope) - { - case ScopeCrossDevice: - case ScopeDevice: - bar_stmt += "memory_scope_device"; - break; - - case ScopeSubgroup: - case ScopeInvocation: - bar_stmt += "memory_scope_simdgroup"; - break; - - case ScopeWorkgroup: - default: - bar_stmt += "memory_scope_threadgroup"; - break; - } - } - bar_stmt += ");"; statement(bar_stmt); @@ -6735,7 +6857,7 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla // If threadgroup storage qualifiers are *not* used: // Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier. - if (lhs_thread && rhs_thread && !use_builtin_array) + if (lhs_thread && rhs_thread && !using_builtin_array()) { statement(lhs, " = ", to_expression(rhs_id), ";"); } @@ -6789,9 +6911,9 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla SPIRV_CROSS_THROW("Unknown storage class used for copying arrays."); // Pass internal array of spvUnsafeArray<> into wrapper functions - if (lhs_thread) + if (lhs_thread && !msl_options.force_native_arrays) statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".elements, ", to_expression(rhs_id), ");"); - else if (rhs_thread) + else if (rhs_thread && !msl_options.force_native_arrays) statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ".elements);"); else statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");"); @@ -7241,11 +7363,31 @@ void CompilerMSL::emit_function_prototype(SPIRFunction &func, const Bitset &) auto &type = get(func.return_type); - decl += func_type_decl(type); + if (!type.array.empty() && msl_options.force_native_arrays) + { + // We cannot return native arrays in MSL, so "return" through an out variable. + decl += "void"; + } + else + { + decl += func_type_decl(type); + } + decl += " "; decl += to_name(func.self); decl += "("; + if (!type.array.empty() && msl_options.force_native_arrays) + { + // Fake arrays returns by writing to an out array instead. + decl += "thread "; + decl += type_to_glsl(type); + decl += " (&SPIRV_Cross_return_value)"; + decl += type_to_array_glsl(type); + if (!func.arguments.empty()) + decl += ", "; + } + if (processing_entry_point) { if (msl_options.argument_buffers) @@ -8190,7 +8332,29 @@ string CompilerMSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_ if (is_dynamic_img_sampler && !arg_is_dynamic_img_sampler) arg_str = join("spvDynamicImageSampler<", type_to_glsl(get(type.image.type)), ">("); - arg_str += CompilerGLSL::to_func_call_arg(arg, id); + auto *c = maybe_get(id); + if (msl_options.force_native_arrays && c && !get(c->constant_type).array.empty()) + { + // If we are passing a constant array directly to a function for some reason, + // the callee will expect an argument in thread const address space + // (since we can only bind to arrays with references in MSL). + // To resolve this, we must emit a copy in this address space. + // This kind of code gen should be rare enough that performance is not a real concern. + // Inline the SPIR-V to avoid this kind of suboptimal codegen. + // + // We risk calling this inside a continue block (invalid code), + // so just create a thread local copy in the current function. + arg_str = join("_", id, "_array_copy"); + auto &constants = current_function->constant_arrays_needed_on_stack; + auto itr = find(begin(constants), end(constants), ID(id)); + if (itr == end(constants)) + { + force_recompile(); + constants.push_back(id); + } + } + else + arg_str += CompilerGLSL::to_func_call_arg(arg, id); // Need to check the base variable in case we need to apply a qualified alias. uint32_t var_id = 0; @@ -8465,9 +8629,9 @@ string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_ // address space. // Array of resources should also be declared as builtin arrays. if (has_member_decoration(type.self, index, DecorationOffset)) - use_builtin_array = true; + is_using_builtin_array = true; else if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary)) - use_builtin_array = true; + is_using_builtin_array = true; if (member_is_packed_physical_type(type, index)) { @@ -8523,14 +8687,14 @@ string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_ { BuiltIn builtin = BuiltInMax; if (is_member_builtin(type, index, &builtin)) - use_builtin_array = true; + is_using_builtin_array = true; array_type = type_to_array_glsl(physical_type); } auto result = join(pack_pfx, type_to_glsl(*declared_type, orig_id), " ", qualifier, to_member_name(type, index), member_attribute_qualifier(type, index), array_type, ";"); - use_builtin_array = false; + is_using_builtin_array = false; return result; } @@ -8825,12 +8989,23 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in switch (builtin) { case BuiltInFragStencilRefEXT: + // Similar to PointSize, only mark FragStencilRef if there's a stencil buffer. + // Some shaders may include a FragStencilRef builtin even when used to render + // without a stencil attachment, and Metal will reject this builtin + // when compiling the shader into a render pipeline that does not set + // stencilAttachmentPixelFormat. + if (!msl_options.enable_frag_stencil_ref_builtin) + return ""; if (!msl_options.supports_msl_version(2, 1)) SPIRV_CROSS_THROW("Stencil export only supported in MSL 2.1 and up."); return string(" [[") + builtin_qualifier(builtin) + "]]"; - case BuiltInSampleMask: case BuiltInFragDepth: + // Ditto FragDepth. + if (!msl_options.enable_frag_depth_builtin) + return ""; + /* fallthrough */ + case BuiltInSampleMask: return string(" [[") + builtin_qualifier(builtin) + "]]"; default: @@ -8838,6 +9013,9 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in } } uint32_t locn = get_ordered_member_location(type.self, index); + // Metal will likely complain about missing color attachments, too. + if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn))) + return ""; if (locn != k_unknown_location && has_member_decoration(type.self, index, DecorationIndex)) return join(" [[color(", locn, "), index(", get_member_decoration(type.self, index, DecorationIndex), ")]]"); @@ -9407,7 +9585,7 @@ void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args) SPIRV_CROSS_THROW("Unsized arrays of buffers are not supported in MSL."); // Allow Metal to use the array template to make arrays a value type - use_builtin_array = true; + is_using_builtin_array = true; buffer_arrays.push_back(var_id); for (uint32_t i = 0; i < array_size; ++i) { @@ -9420,7 +9598,7 @@ void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args) ep_args += ", raster_order_group(0)"; ep_args += "]]"; } - use_builtin_array = false; + is_using_builtin_array = false; } else { @@ -9902,6 +10080,11 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base // If we did not explicitly remap, allocate bindings on demand. // We cannot reliably use Binding decorations since SPIR-V and MSL's binding models are very different. + bool allocate_argument_buffer_ids = false; + + if (var.storage != StorageClassPushConstant) + allocate_argument_buffer_ids = descriptor_set_is_argument_buffer(var_desc_set); + uint32_t binding_stride = 1; auto &type = get(var.basetype); for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) @@ -9912,20 +10095,11 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base // If a binding has not been specified, revert to incrementing resource indices. uint32_t resource_index; - bool allocate_argument_buffer_ids = false; - uint32_t desc_set = 0; - - if (var.storage != StorageClassPushConstant) - { - desc_set = get_decoration(var.self, DecorationDescriptorSet); - allocate_argument_buffer_ids = descriptor_set_is_argument_buffer(desc_set); - } - if (allocate_argument_buffer_ids) { // Allocate from a flat ID binding space. - resource_index = next_metal_resource_ids[desc_set]; - next_metal_resource_ids[desc_set] += binding_stride; + resource_index = next_metal_resource_ids[var_desc_set]; + next_metal_resource_ids[var_desc_set] += binding_stride; } else { @@ -9990,9 +10164,9 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) // Allow Metal to use the array template to make arrays a value type string address_space = get_argument_address_space(var); bool builtin = is_builtin_variable(var); - use_builtin_array = builtin; + is_using_builtin_array = builtin; if (address_space == "threadgroup") - use_builtin_array = true; + is_using_builtin_array = true; if (var.basevariable && (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id)) decl += type_to_glsl(type, arg.id); @@ -10000,7 +10174,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) decl += builtin_type_decl(static_cast(get_decoration(arg.id, DecorationBuiltIn)), arg.id); else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type)) { - use_builtin_array = true; + is_using_builtin_array = true; decl += join(type_to_glsl(type, arg.id), "*"); } else if (is_dynamic_img_sampler) @@ -10018,10 +10192,34 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) (storage == StorageClassFunction || storage == StorageClassGeneric)) { // If the argument is a pure value and not an opaque type, we will pass by value. - if (!address_space.empty()) - decl = join(address_space, " ", decl); - decl += " "; - decl += to_expression(name_id); + if (msl_options.force_native_arrays && is_array(type)) + { + // We are receiving an array by value. This is problematic. + // We cannot be sure of the target address space since we are supposed to receive a copy, + // but this is not possible with MSL without some extra work. + // We will have to assume we're getting a reference in thread address space. + // If we happen to get a reference in constant address space, the caller must emit a copy and pass that. + // Thread const therefore becomes the only logical choice, since we cannot "create" a constant array from + // non-constant arrays, but we can create thread const from constant. + decl = string("thread const ") + decl; + decl += " (&"; + const char *restrict_kw = to_restrict(name_id); + if (*restrict_kw) + { + decl += " "; + decl += restrict_kw; + } + decl += to_expression(name_id); + decl += ")"; + decl += type_to_array_glsl(type); + } + else + { + if (!address_space.empty()) + decl = join(address_space, " ", decl); + decl += " "; + decl += to_expression(name_id); + } } else if (is_array(type) && !type_is_image) { @@ -10097,7 +10295,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) decl += "* " + to_expression(name_id) + "_atomic"; } - use_builtin_array = false; + is_using_builtin_array = false; return decl; } @@ -10395,19 +10593,31 @@ void CompilerMSL::replace_illegal_names() }; ir.for_each_typed_id([&](uint32_t self, SPIRVariable &) { - auto &dec = ir.meta[self].decoration; + auto *meta = ir.find_meta(self); + if (!meta) + return; + + auto &dec = meta->decoration; if (keywords.find(dec.alias) != end(keywords)) dec.alias += "0"; }); ir.for_each_typed_id([&](uint32_t self, SPIRFunction &) { - auto &dec = ir.meta[self].decoration; + auto *meta = ir.find_meta(self); + if (!meta) + return; + + auto &dec = meta->decoration; if (illegal_func_names.find(dec.alias) != end(illegal_func_names)) dec.alias += "0"; }); ir.for_each_typed_id([&](uint32_t self, SPIRType &) { - for (auto &mbr_dec : ir.meta[self].members) + auto *meta = ir.find_meta(self); + if (!meta) + return; + + for (auto &mbr_dec : meta->members) if (keywords.find(mbr_dec.alias) != end(keywords)) mbr_dec.alias += "0"; }); @@ -10436,7 +10646,12 @@ string CompilerMSL::to_member_reference(uint32_t base, const SPIRType &type, uin if (var) { - bool is_buffer_variable = var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer; + // Only allow -> dereference for block types. This is so we get expressions like + // buffer[i]->first_member.second_member, rather than buffer[i]->first->second. + bool is_block = has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + bool is_buffer_variable = + is_block && (var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer); declared_as_pointer = is_buffer_variable && is_array(get(var->basetype)); } @@ -10570,7 +10785,7 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id) if (type.vecsize > 1) type_name += to_string(type.vecsize); - if (type.array.empty() || use_builtin_array) + if (type.array.empty() || using_builtin_array()) { return type_name; } @@ -10606,7 +10821,7 @@ string CompilerMSL::type_to_array_glsl(const SPIRType &type) } default: { - if (use_builtin_array) + if (using_builtin_array()) return CompilerGLSL::type_to_array_glsl(type); else return ""; @@ -10619,12 +10834,12 @@ std::string CompilerMSL::variable_decl(const SPIRVariable &variable) { if (variable.storage == StorageClassWorkgroup) { - use_builtin_array = true; + is_using_builtin_array = true; } std::string expr = CompilerGLSL::variable_decl(variable); if (variable.storage == StorageClassWorkgroup) { - use_builtin_array = false; + is_using_builtin_array = false; } return expr; } @@ -10860,6 +11075,11 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i) if (!msl_options.supports_msl_version(2)) SPIRV_CROSS_THROW("Subgroups are only supported in Metal 2.0 and up."); + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + if (msl_options.is_ios()) { switch (op) @@ -10882,7 +11102,7 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i) switch (op) { default: - SPIRV_CROSS_THROW("Subgroup ops beyond broadcast and shuffle on macOS require Metal 2.0 and up."); + SPIRV_CROSS_THROW("Subgroup ops beyond broadcast and shuffle on macOS require Metal 2.1 and up."); case OpGroupNonUniformBroadcast: case OpGroupNonUniformShuffle: case OpGroupNonUniformShuffleXor: @@ -11010,6 +11230,7 @@ case OpGroupNonUniform##op: \ MSL_GROUP_OP(IMul, product) #undef MSL_GROUP_OP // The others, unfortunately, don't support InclusiveScan or ExclusiveScan. + #define MSL_GROUP_OP(op, msl_op) \ case OpGroupNonUniform##op: \ { \ @@ -11032,12 +11253,36 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } + +#define MSL_GROUP_OP_CAST(op, msl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "simd_" #msl_op, type, type); \ + else if (operation == GroupOperationInclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support InclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationExclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support ExclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + /* Only cluster sizes of 4 are supported. */ \ + uint32_t cluster_size = get(ops[5]).scalar(); \ + if (cluster_size != 4) \ + SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ + emit_unary_func_op_cast(result_type, id, ops[4], "quad_" #msl_op, type, type); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + MSL_GROUP_OP(FMin, min) MSL_GROUP_OP(FMax, max) - MSL_GROUP_OP(SMin, min) - MSL_GROUP_OP(SMax, max) - MSL_GROUP_OP(UMin, min) - MSL_GROUP_OP(UMax, max) + MSL_GROUP_OP_CAST(SMin, min, int_type) + MSL_GROUP_OP_CAST(SMax, max, int_type) + MSL_GROUP_OP_CAST(UMin, min, uint_type) + MSL_GROUP_OP_CAST(UMax, max, uint_type) MSL_GROUP_OP(BitwiseAnd, and) MSL_GROUP_OP(BitwiseOr, or) MSL_GROUP_OP(BitwiseXor, xor) @@ -11045,6 +11290,8 @@ case OpGroupNonUniform##op: \ MSL_GROUP_OP(LogicalOr, or) MSL_GROUP_OP(LogicalXor, xor) // clang-format on +#undef MSL_GROUP_OP +#undef MSL_GROUP_OP_CAST case OpGroupNonUniformQuadSwap: { @@ -11099,6 +11346,11 @@ string CompilerMSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in } } +bool CompilerMSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + return false; +} + // Returns an MSL string identifying the name of a SPIR-V builtin. // Output builtins are qualified with the name of the stage out structure. string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) @@ -11224,13 +11476,17 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) if (!msl_options.supports_msl_version(2, 0)) SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); /* fallthrough */ + case BuiltInFragDepth: + case BuiltInFragStencilRefEXT: + if ((builtin == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) || + (builtin == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin)) + break; + /* fallthrough */ case BuiltInPosition: case BuiltInPointSize: case BuiltInClipDistance: case BuiltInCullDistance: case BuiltInLayer: - case BuiltInFragDepth: - case BuiltInFragStencilRefEXT: case BuiltInSampleMask: if (get_execution_model() == ExecutionModelTessellationControl) break; @@ -11654,7 +11910,7 @@ uint32_t CompilerMSL::get_declared_type_matrix_stride_msl(const SPIRType &type, // For packed matrices, we just use the size of the vector type. // Otherwise, MatrixStride == alignment, which is the size of the underlying vector type. if (packed) - return (type.width / 8) * (row_major ? type.columns : type.vecsize); + return (type.width / 8) * ((row_major && type.columns > 1) ? type.columns : type.vecsize); else return get_declared_type_alignment_msl(type, false, row_major); } @@ -11732,7 +11988,7 @@ uint32_t CompilerMSL::get_declared_type_size_msl(const SPIRType &type, bool is_p uint32_t vecsize = type.vecsize; uint32_t columns = type.columns; - if (row_major) + if (row_major && columns > 1) swap(vecsize, columns); if (vecsize == 3) @@ -11793,7 +12049,7 @@ uint32_t CompilerMSL::get_declared_type_alignment_msl(const SPIRType &type, bool else { // This is the general rule for MSL. Size == alignment. - uint32_t vecsize = row_major ? type.columns : type.vecsize; + uint32_t vecsize = (row_major && type.columns > 1) ? type.columns : type.vecsize; return (type.width / 8) * (vecsize == 3 ? 4 : vecsize); } } @@ -12349,7 +12605,7 @@ void CompilerMSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr } } -std::string CompilerMSL::to_initializer_expression(const SPIRVariable &var) +string CompilerMSL::to_initializer_expression(const SPIRVariable &var) { // We risk getting an array initializer here with MSL. If we have an array. // FIXME: We cannot handle non-constant arrays being initialized. @@ -12362,6 +12618,11 @@ std::string CompilerMSL::to_initializer_expression(const SPIRVariable &var) return CompilerGLSL::to_initializer_expression(var); } +string CompilerMSL::to_zero_initialized_expression(uint32_t) +{ + return "{}"; +} + bool CompilerMSL::descriptor_set_is_argument_buffer(uint32_t desc_set) const { if (!msl_options.argument_buffers) @@ -12394,6 +12655,7 @@ void CompilerMSL::analyze_argument_buffers() uint32_t plane; }; SmallVector resources_in_set[kMaxArgumentBuffers]; + SmallVector inline_block_vars; bool set_needs_swizzle_buffer[kMaxArgumentBuffers] = {}; bool set_needs_buffer_sizes[kMaxArgumentBuffers] = {}; @@ -12426,6 +12688,7 @@ void CompilerMSL::analyze_argument_buffers() } } + uint32_t binding = get_decoration(var_id, DecorationBinding); if (type.basetype == SPIRType::SampledImage) { add_resource_name(var_id); @@ -12448,9 +12711,14 @@ void CompilerMSL::analyze_argument_buffers() { &var, to_sampler_expression(var_id), SPIRType::Sampler, sampler_resource_index, 0 }); } } + else if (inline_uniform_blocks.count(SetBindingPair{ desc_set, binding })) + { + inline_block_vars.push_back(var_id); + } else if (!constexpr_sampler) { // constexpr samplers are not declared as resources. + // Inline uniform blocks are always emitted at the end. if (!msl_options.is_ios() || type.basetype != SPIRType::Image || type.image.sampled != 2) { add_resource_name(var_id); @@ -12525,6 +12793,16 @@ void CompilerMSL::analyze_argument_buffers() } } + // Now add inline uniform blocks. + for (uint32_t var_id : inline_block_vars) + { + auto &var = get(var_id); + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + add_resource_name(var_id); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::Struct, get_metal_resource_index(var, SPIRType::Struct), 0 }); + } + for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++) { auto &resources = resources_in_set[desc_set]; @@ -12622,6 +12900,12 @@ void CompilerMSL::analyze_argument_buffers() buffer_type.member_types.push_back(var.basetype); buffers_requiring_dynamic_offset[pair].second = var.self; } + else if (inline_uniform_blocks.count(pair)) + { + // Put the buffer block itself into the argument buffer. + buffer_type.member_types.push_back(get_variable_data_type_id(var)); + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } else { // Resources will be declared as pointers not references, so automatically dereference as appropriate. @@ -12642,34 +12926,20 @@ void CompilerMSL::analyze_argument_buffers() } } -bool CompilerMSL::SetBindingPair::operator==(const SetBindingPair &other) const +void CompilerMSL::activate_argument_buffer_resources() { - return desc_set == other.desc_set && binding == other.binding; + // For ABI compatibility, force-enable all resources which are part of argument buffers. + ir.for_each_typed_id([&](uint32_t self, const SPIRVariable &) { + if (!has_decoration(self, DecorationDescriptorSet)) + return; + + uint32_t desc_set = get_decoration(self, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + active_interface_variables.insert(self); + }); } -bool CompilerMSL::SetBindingPair::operator<(const SetBindingPair &other) const +bool CompilerMSL::using_builtin_array() const { - return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); -} - -bool CompilerMSL::StageSetBinding::operator==(const StageSetBinding &other) const -{ - return model == other.model && desc_set == other.desc_set && binding == other.binding; -} - -size_t CompilerMSL::InternalHasher::operator()(const SetBindingPair &value) const -{ - // Quality of hash doesn't really matter here. - auto hash_set = std::hash()(value.desc_set); - auto hash_binding = std::hash()(value.binding); - return (hash_set * 0x10001b31) ^ hash_binding; -} - -size_t CompilerMSL::InternalHasher::operator()(const StageSetBinding &value) const -{ - // Quality of hash doesn't really matter here. - auto hash_model = std::hash()(value.model); - auto hash_set = std::hash()(value.desc_set); - auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set; - return (tmp_hash * 0x10001b31) ^ value.binding; + return msl_options.force_native_arrays || is_using_builtin_array; } diff --git a/src/libraries/spirv_cross/spirv_msl.hpp b/src/libraries/spirv_cross/spirv_msl.hpp index 79b1d9490..50a795de5 100644 --- a/src/libraries/spirv_cross/spirv_msl.hpp +++ b/src/libraries/spirv_cross/spirv_msl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The Brenwill Workshop Ltd. + * Copyright 2016-2020 The Brenwill Workshop Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -218,11 +218,13 @@ struct MSLConstexprSampler // Special constant used in a MSLResourceBinding desc_set // element to indicate the bindings for the push constants. -static const uint32_t kPushConstDescSet = ~(0u); +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstDescSet = ResourceBindingPushConstantDescriptorSet; // Special constant used in a MSLResourceBinding binding // element to indicate the bindings for the push constants. -static const uint32_t kPushConstBinding = 0; +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstBinding = ResourceBindingPushConstantBinding; // Special constant used in a MSLResourceBinding binding // element to indicate the buffer binding for swizzle buffers. @@ -266,7 +268,10 @@ public: uint32_t dynamic_offsets_buffer_index = 23; uint32_t shader_input_wg_index = 0; uint32_t device_index = 0; + uint32_t enable_frag_output_mask = 0xffffffff; bool enable_point_size_builtin = true; + bool enable_frag_depth_builtin = true; + bool enable_frag_stencil_ref_builtin = true; bool disable_rasterization = false; bool capture_output_to_buffer = false; bool swizzle_texture_samples = false; @@ -305,6 +310,20 @@ public: // Requires MSL 2.1, use the native support for texel buffers. bool texture_buffer_native = false; + // Forces all resources which are part of an argument buffer to be considered active. + // This ensures ABI compatibility between shaders where some resources might be unused, + // and would otherwise declare a different IAB. + bool force_active_argument_buffer_resources = false; + + // Forces the use of plain arrays, which works around certain driver bugs on certain versions + // of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210. + // May reduce performance in scenarios where arrays are copied around as value-types. + bool force_native_arrays = false; + + // If a shader writes clip distance, also emit user varyings which + // can be read in subsequent stages. + bool enable_clip_distance_user_varying = true; + bool is_ios() { return platform == iOS; @@ -425,6 +444,13 @@ public: // an offset taken from the dynamic offset buffer. void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index); + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. This function marks that resource as an inline uniform block + // (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT). This function only has any effect if argument buffers + // are enabled. If so, the buffer block will be directly embedded into the argument + // buffer, instead of being referenced indirectly via pointer. + void add_inline_uniform_block(uint32_t desc_set, uint32_t binding); + // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets. // This corresponds to VK_KHR_push_descriptor in Vulkan. void add_discrete_descriptor_set(uint32_t desc_set); @@ -440,7 +466,7 @@ public: // Constexpr samplers are always assumed to be emitted. // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped // by remap_constexpr_sampler(_by_binding). - bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding); + bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; // This must only be called after a successful call to CompilerMSL::compile(). // For a variable resource ID obtained through reflection API, report the automatically assigned resource index. @@ -600,6 +626,7 @@ protected: uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample, uint32_t minlod, bool *p_forward) override; std::string to_initializer_expression(const SPIRVariable &var) override; + std::string to_zero_initialized_expression(uint32_t type_id) override; std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, bool is_packed, bool row_major) override; @@ -608,6 +635,7 @@ protected: bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override; std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_id, uint32_t id, uint32_t op0) override; bool skip_argument(uint32_t id) const override; std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain) override; std::string to_qualifiers_glsl(uint32_t id) override; @@ -786,28 +814,6 @@ protected: std::set typedef_lines; SmallVector vars_needing_early_declaration; - struct SetBindingPair - { - uint32_t desc_set; - uint32_t binding; - bool operator==(const SetBindingPair &other) const; - bool operator<(const SetBindingPair &other) const; - }; - - struct StageSetBinding - { - spv::ExecutionModel model; - uint32_t desc_set; - uint32_t binding; - bool operator==(const StageSetBinding &other) const; - }; - - struct InternalHasher - { - size_t operator()(const SetBindingPair &value) const; - size_t operator()(const StageSetBinding &value) const; - }; - std::unordered_map, InternalHasher> resource_bindings; uint32_t next_metal_resource_index_buffer = 0; @@ -835,7 +841,10 @@ protected: bool has_sampled_images = false; bool builtin_declaration = false; // Handle HLSL-style 0-based vertex/instance index. - bool use_builtin_array = false; // Force the use of C style array declaration. + + bool is_using_builtin_array = false; // Force the use of C style array declaration. + bool using_builtin_array() const; + bool is_rasterization_disabled = false; bool capture_output_to_buffer = false; bool needs_swizzle_buffer_def = false; @@ -869,6 +878,10 @@ protected: // Must be ordered since array is in a specific order. std::map> buffers_requiring_dynamic_offset; + SmallVector disabled_frag_outputs; + + std::unordered_set inline_uniform_blocks; + uint32_t argument_buffer_ids[kMaxArgumentBuffers]; uint32_t argument_buffer_discrete_mask = 0; uint32_t argument_buffer_device_storage_mask = 0; @@ -883,6 +896,8 @@ protected: void add_spv_func_and_recompile(SPVFuncImpl spv_func); + void activate_argument_buffer_resources(); + // OpcodeHandler that handles several MSL preprocessing operations. struct OpCodePreprocessor : OpcodeHandler { diff --git a/src/libraries/spirv_cross/spirv_parser.cpp b/src/libraries/spirv_cross/spirv_parser.cpp index 08dcff90a..e65e3608e 100644 --- a/src/libraries/spirv_cross/spirv_parser.cpp +++ b/src/libraries/spirv_cross/spirv_parser.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019 Arm Limited + * Copyright 2018-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,6 +86,11 @@ void Parser::parse() SPIRV_CROSS_THROW("Invalid SPIRV format."); uint32_t bound = s[3]; + + const uint32_t MaximumNumberOfIDs = 0x3fffff; + if (bound > MaximumNumberOfIDs) + SPIRV_CROSS_THROW("ID bound exceeds limit of 0x3fffff.\n"); + ir.set_id_bounds(bound); uint32_t offset = 5; @@ -678,11 +683,19 @@ void Parser::parse(const Instruction &instruction) break; } - case OpTypeAccelerationStructureNV: + case OpTypeAccelerationStructureKHR: { uint32_t id = ops[0]; auto &type = set(id); - type.basetype = SPIRType::AccelerationStructureNV; + type.basetype = SPIRType::AccelerationStructure; + break; + } + + case OpTypeRayQueryProvisionalKHR: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::RayQuery; break; } @@ -703,15 +716,6 @@ void Parser::parse(const Instruction &instruction) } set(id, type, storage, initializer); - - // hlsl based shaders don't have those decorations. force them and then reset when reading/writing images - auto &ttype = get(type); - if (ttype.basetype == SPIRType::BaseType::Image) - { - ir.set_decoration(id, DecorationNonWritable); - ir.set_decoration(id, DecorationNonReadable); - } - break; } @@ -775,7 +779,7 @@ void Parser::parse(const Instruction &instruction) { uint32_t id = ops[1]; uint32_t type = ops[0]; - make_constant_null(id, type); + ir.make_constant_null(id, type, true); break; } @@ -1140,46 +1144,4 @@ bool Parser::variable_storage_is_aliased(const SPIRVariable &v) const return !is_restrict && (ssbo || image || counter); } - -void Parser::make_constant_null(uint32_t id, uint32_t type) -{ - auto &constant_type = get(type); - - if (constant_type.pointer) - { - auto &constant = set(id, type); - constant.make_null(constant_type); - } - else if (!constant_type.array.empty()) - { - assert(constant_type.parent_type); - uint32_t parent_id = ir.increase_bound_by(1); - make_constant_null(parent_id, constant_type.parent_type); - - if (!constant_type.array_size_literal.back()) - SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal."); - - SmallVector elements(constant_type.array.back()); - for (uint32_t i = 0; i < constant_type.array.back(); i++) - elements[i] = parent_id; - set(id, type, elements.data(), uint32_t(elements.size()), false); - } - else if (!constant_type.member_types.empty()) - { - uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size())); - SmallVector elements(constant_type.member_types.size()); - for (uint32_t i = 0; i < constant_type.member_types.size(); i++) - { - make_constant_null(member_ids + i, constant_type.member_types[i]); - elements[i] = member_ids + i; - } - set(id, type, elements.data(), uint32_t(elements.size()), false); - } - else - { - auto &constant = set(id, type); - constant.make_null(constant_type); - } -} - } // namespace SPIRV_CROSS_NAMESPACE diff --git a/src/libraries/spirv_cross/spirv_parser.hpp b/src/libraries/spirv_cross/spirv_parser.hpp index ef2c1b986..dafa3e868 100644 --- a/src/libraries/spirv_cross/spirv_parser.hpp +++ b/src/libraries/spirv_cross/spirv_parser.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019 Arm Limited + * Copyright 2018-2020 Arm Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,7 +87,6 @@ private: bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; bool variable_storage_is_aliased(const SPIRVariable &v) const; - void make_constant_null(uint32_t id, uint32_t type); }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/src/libraries/spirv_cross/spirv_reflect.cpp b/src/libraries/spirv_cross/spirv_reflect.cpp index 1e8f615c7..97d640c56 100644 --- a/src/libraries/spirv_cross/spirv_reflect.cpp +++ b/src/libraries/spirv_cross/spirv_reflect.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019 Bradley Austin Davis + * Copyright 2018-2020 Bradley Austin Davis * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -358,15 +358,16 @@ void CompilerReflection::emit_type_array(const SPIRType &type) for (const auto &value : type.array) json_stream->emit_json_array_value(value); json_stream->end_json_array(); + + json_stream->emit_json_key_array("array_size_is_literal"); + for (const auto &value : type.array_size_literal) + json_stream->emit_json_array_value(value); + json_stream->end_json_array(); } } void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index) { - auto flags = combined_decoration_for_member(type, index); - if (flags.get(DecorationRowMajor)) - json_stream->emit_json_key_value("row_major", true); - auto &membertype = get(type.member_types[index]); emit_type_array(membertype); auto &memb = ir.meta[type.self].members; @@ -377,6 +378,16 @@ void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint3 json_stream->emit_json_key_value("location", dec.location); if (dec.decoration_flags.get(DecorationOffset)) json_stream->emit_json_key_value("offset", dec.offset); + + // Array stride is a property of the array type, not the struct. + if (has_decoration(type.member_types[index], DecorationArrayStride)) + json_stream->emit_json_key_value("array_stride", + get_decoration(type.member_types[index], DecorationArrayStride)); + + if (dec.decoration_flags.get(DecorationMatrixStride)) + json_stream->emit_json_key_value("matrix_stride", dec.matrix_stride); + if (dec.decoration_flags.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); } } @@ -590,8 +601,10 @@ void CompilerReflection::emit_specialization_constants() auto &c = get(spec_const.id); auto type = get(c.constant_type); json_stream->begin_json_object(); + json_stream->emit_json_key_value("name", get_name(spec_const.id)); json_stream->emit_json_key_value("id", spec_const.constant_id); json_stream->emit_json_key_value("type", type_to_glsl(type)); + json_stream->emit_json_key_value("variable_id", spec_const.id); switch (type.basetype) { case SPIRType::UInt: diff --git a/src/libraries/spirv_cross/spirv_reflect.hpp b/src/libraries/spirv_cross/spirv_reflect.hpp index 5a228a683..844d3f1e9 100644 --- a/src/libraries/spirv_cross/spirv_reflect.hpp +++ b/src/libraries/spirv_cross/spirv_reflect.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019 Bradley Austin Davis + * Copyright 2018-2020 Bradley Austin Davis * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.