Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 10 additions & 4 deletions tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<VKBuiltInAttr>()) {
*value = spvBuilder.getConstantNull(stageVarData.type);
Comment thread
apollo-2006 marked this conversation as resolved.
Resolved
return true;
}

// Special handling of certain mappings between HLSL semantics and
// SPIR-V builtins:
// * SV_CullDistance/SV_ClipDistance are outsourced to GlPerVertex.
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Comment thread
apollo-2006 marked this conversation as resolved.
Resolved
// 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
Original file line number Diff line number Diff line change
@@ -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;
}