diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 082a606687b..b2ce18f0c68 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -26,6 +26,8 @@ The included licenses apply to the following files: descriptor heaps with a literal `ArrayStride`. The value must be a power of two in `[8, 256]`. Both flags require `-spirv` and `-fspv-use-descriptor-heap` and are rejected otherwise. +- `SV_CullPrimitive` as a pixel shader input is now replaced with 0, matching + DXIL, instead of producing invalid SPIR-V. Place release notes for the upcoming release below this line and remove this line upon naming the release. Refer to previous for appropriate section names. diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 46967298c70..b7d13d53dd8 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -3881,6 +3881,16 @@ bool DeclResultIdMapper::createStageVars(StageVarDataBundle &stageVarData, return false; } + // DXIL replaces a pixel shader SV_CullPrimitive input with 0, and + // CullPrimitiveEXT can't be used in fragment shaders. An explicit + // vk::builtin still takes precedence. + if (semanticKind == hlsl::Semantic::Kind::CullPrimitive && + sigPointKind == hlsl::SigPoint::Kind::PSIn && + !stageVarData.decl->hasAttr()) { + *value = spvBuilder.getConstantNull(stageVarData.type); + return true; + } + // Special handling of certain mappings between HLSL semantics and // SPIR-V builtins: // * SV_CullDistance/SV_ClipDistance are outsourced to GlPerVertex. @@ -4786,10 +4796,6 @@ SpirvVariable *DeclResultIdMapper::createSpirvStageVar( // be used as MSOut. case hlsl::Semantic::Kind::CullPrimitive: { switch (sigPointKind) { - case hlsl::SigPoint::Kind::PSIn: - stageVar->setIsSpirvBuiltin(); - return spvBuilder.addStageBuiltinVar(type, sc, BuiltIn::CullPrimitiveEXT, - isPrecise, srcLoc); case hlsl::SigPoint::Kind::MSPOut: stageVar->setIsSpirvBuiltin(); return spvBuilder.addStageBuiltinVar(type, sc, BuiltIn::CullPrimitiveEXT, diff --git a/tools/clang/test/CodeGenSPIRV/meshshading.ext.cullprimitive.ps.hlsl b/tools/clang/test/CodeGenSPIRV/meshshading.ext.cullprimitive.ps.hlsl new file mode 100644 index 00000000000..5e2cbefd0e5 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/meshshading.ext.cullprimitive.ps.hlsl @@ -0,0 +1,42 @@ +// RUN: %dxc -T ps_6_5 -E main %s -spirv -DCULL_TYPE=bool | FileCheck %s +// RUN: %dxc -T ps_6_5 -E main %s -spirv -DCULL_TYPE=uint | FileCheck %s +// RUN: %dxc -T ps_6_5 -E main %s -spirv -DCULL_TYPE=bool2 | FileCheck %s +// RUN: %dxc -T ps_6_5 -E main %s -spirv -DCULL_ARRAY | FileCheck %s + +// RUN: %dxc -T ps_6_5 -E main %s -spirv -fcgl -DCULL_TYPE=bool | FileCheck %s --check-prefix=BOOL +// RUN: %dxc -T ps_6_5 -E main %s -spirv -fcgl -DCULL_TYPE=uint | FileCheck %s --check-prefix=UINT +// RUN: %dxc -T ps_6_5 -E main %s -spirv -fcgl -DCULL_TYPE=bool2 | FileCheck %s --check-prefix=BOOL2 +// RUN: %dxc -T ps_6_5 -E main %s -spirv -fcgl -DCULL_ARRAY | FileCheck %s --check-prefix=ARRAY +// RUN: %dxc -T ps_6_5 -E main %s -spirv -fcgl -DCULL_BUILTIN | FileCheck %s --check-prefix=BUILTIN + +// SV_CullPrimitive as a pixel shader input is replaced with 0, like DXIL. +// DXIL doesn't check the type here, so aggregates are replaced too. + +// CHECK-NOT: CullPrimitiveEXT +// CHECK: OpEntryPoint Fragment %main "main" %out_var_SV_Target +// CHECK-NOT: CullPrimitiveEXT + +// BOOL: [[null:%[0-9]+]] = OpConstantNull %bool +// BOOL: OpStore %param_var_cull [[null]] + +// UINT: [[null:%[0-9]+]] = OpConstantNull %uint +// UINT: OpStore %param_var_cull [[null]] + +// BOOL2: [[null:%[0-9]+]] = OpConstantNull %v2bool +// BOOL2: OpStore %param_var_cull [[null]] + +// ARRAY: [[null:%[0-9]+]] = OpConstantNull %_arr_bool_uint_2 +// ARRAY: OpStore %param_var_cull [[null]] + +// An explicit vk::builtin is not replaced. +// BUILTIN-NOT: OpConstantNull +// BUILTIN: OpLoad %bool %HelperInvocation + +#ifdef CULL_ARRAY +float4 main(bool cull[2] : SV_CullPrimitive) : SV_Target { return cull[1]; } +#elif defined(CULL_BUILTIN) +float4 main([[vk::builtin("HelperInvocation")]] bool cull : SV_CullPrimitive) + : SV_Target { return cull; } +#else +float4 main(CULL_TYPE cull : SV_CullPrimitive) : SV_Target { return cull.x; } +#endif diff --git a/tools/clang/test/CodeGenSPIRV/meshshading.ext.cullprimitive.ps.shared-struct.hlsl b/tools/clang/test/CodeGenSPIRV/meshshading.ext.cullprimitive.ps.shared-struct.hlsl new file mode 100644 index 00000000000..46f2217806a --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/meshshading.ext.cullprimitive.ps.shared-struct.hlsl @@ -0,0 +1,64 @@ +// RUN: %dxc -T ms_6_5 -E ms_main %s -spirv -fspv-target-env=vulkan1.3 -DCULL_FIRST | FileCheck %s --check-prefix=MS +// RUN: %dxc -T ms_6_5 -E ms_main %s -spirv -fspv-target-env=vulkan1.3 | FileCheck %s --check-prefix=MS +// RUN: %dxc -T ps_6_5 -E ps_main %s -spirv -DCULL_FIRST | FileCheck %s --check-prefix=PS +// RUN: %dxc -T ps_6_5 -E ps_main %s -spirv | FileCheck %s --check-prefix=PS +// RUN: %dxc -T ps_6_5 -E ps_main %s -spirv -fcgl -DCULL_FIRST | FileCheck %s --check-prefix=FIRST +// RUN: %dxc -T ps_6_5 -E ps_main %s -spirv -fcgl | FileCheck %s --check-prefix=LATER + +// The other members keep the mesh shader's locations, and the 0 lands in the +// right member, whether SV_CullPrimitive is first or not. + +// MS: OpDecorate {{%[0-9]+}} BuiltIn CullPrimitiveEXT +// MS-DAG: OpDecorate %out_var_PRIMID Location 0 +// MS-DAG: OpDecorate %out_var_COLOR0 Location 1 + +// PS-NOT: CullPrimitiveEXT +// PS-DAG: OpDecorate %in_var_PRIMID Location 0 +// PS-DAG: OpDecorate %in_var_COLOR0 Location 1 +// PS-NOT: CullPrimitiveEXT + +// FIRST: [[null:%[0-9]+]] = OpConstantNull %bool +// FIRST: [[id:%[0-9]+]] = OpLoad %uint %in_var_PRIMID +// FIRST: [[col:%[0-9]+]] = OpLoad %v4float %in_var_COLOR0 +// FIRST: OpCompositeConstruct %Prim [[null]] [[id]] [[col]] + +// LATER: [[null:%[0-9]+]] = OpConstantNull %bool +// LATER: [[id:%[0-9]+]] = OpLoad %uint %in_var_PRIMID +// LATER: [[col:%[0-9]+]] = OpLoad %v4float %in_var_COLOR0 +// LATER: OpCompositeConstruct %Prim [[id]] [[null]] [[col]] + +#ifdef CULL_FIRST +struct Prim { + bool cull : SV_CullPrimitive; + uint id : PRIMID; + float4 col : COLOR0; +}; +#else +struct Prim { + uint id : PRIMID; + bool cull : SV_CullPrimitive; + float4 col : COLOR0; +}; +#endif + +struct Vert { + float4 pos : SV_Position; +}; + +[outputtopology("triangle")] +[numthreads(1, 1, 1)] +void ms_main(out vertices Vert verts[3], out indices uint3 tris[1], + out primitives Prim prims[1]) { + SetMeshOutputCounts(3, 1); + verts[0].pos = 0; + verts[1].pos = 0; + verts[2].pos = 0; + tris[0] = uint3(0, 1, 2); + prims[0].cull = false; + prims[0].id = 7; + prims[0].col = 1; +} + +float4 ps_main(Vert v, Prim p) : SV_Target { + return p.cull ? 0 : p.col * p.id; +}