From 6090ac4787f90d40cd2d314c4d69b07f40c3a8a9 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Wed, 10 Jun 2026 13:29:11 +0200 Subject: [PATCH 1/3] add system value tests for HS and DS --- .../Feature/Semantics/DomainSystemValues.test | 254 ++++++++++++++++++ test/Feature/Semantics/HullSystemValues.test | 254 ++++++++++++++++++ 2 files changed, 508 insertions(+) create mode 100644 test/Feature/Semantics/DomainSystemValues.test create mode 100644 test/Feature/Semantics/HullSystemValues.test diff --git a/test/Feature/Semantics/DomainSystemValues.test b/test/Feature/Semantics/DomainSystemValues.test new file mode 100644 index 000000000..2f7b138e4 --- /dev/null +++ b/test/Feature/Semantics/DomainSystemValues.test @@ -0,0 +1,254 @@ +# Bundled domain-shader (DS) system-value test. Exercises: +# * SV_PrimitiveID - DS input (patch index; new coverage) +# * SV_DomainLocation - DS input (the tessellator's (u, v) for this vertex) +# * SV_Position - DS output -> rasterizer -> PS input (pixel center) +# +# SV_PrimitiveID: https://github.com/llvm/wg-hlsl/issues/160 +# SV_DomainLocation / SV_Position (DS): https://github.com/llvm/wg-hlsl/issues/141 +# Clang's HLSL -> DXIL lowering does not yet implement the tessellation stages, +# so this is XFAIL on Clang. Metal has no Hull/Domain stages (see note below). +# +# Geometry: two independent quad patches drawn back-to-back (8 control points, +# PatchControlPoints = 4). The input-assembler hands each patch a distinct +# SV_PrimitiveID (0, 1). Each patch covers one half of a 4x2 render target: +# +# patch 0 -> NDC x in [-1, 0] -> columns 0,1 +# patch 1 -> NDC x in [ 0, 1] -> columns 2,3 +# +# With integer partitioning and all tess factors = 2, each patch is subdivided +# into a 2x2 grid of sub-quads. The sub-quad seams fall at domain (u, v) = 0.5, +# i.e. exactly on a pixel *boundary*, so every pixel center maps to a clean +# domain coordinate of 0.25 or 0.75 on each axis -- identical to the mapping +# proven in test/Graphics/QuadDomainTessellation.test, and exact across +# backends (the readback never lands on a triangle diagonal). +# +# Per patch the four pixel centers map to: +# (u, v) = (0.25, 0.75) (0.75, 0.75) <- top row (NDC +y) +# (0.25, 0.25) (0.75, 0.25) <- bottom row (NDC -y) + +#--- vertex.hlsl +struct VSOutput { + float4 position : POSITION; +}; + +// Pass-through: control points travel unchanged into the Hull stage. The vertex +// attribute supplies only xy; z=0, w=1 are filled by the input assembler. +VSOutput main(float4 position : POSITION) { + VSOutput o; + o.position = position; + return o; +} + +#--- hull.hlsl +struct HSInput { + float4 position : POSITION; +}; + +struct HSOutput { + float4 position : POSITION; +}; + +struct HSPatchConstants { + float Edges[4] : SV_TessFactor; + float Inside[2] : SV_InsideTessFactor; +}; + +HSPatchConstants PatchConstants(InputPatch patch, + uint patchID : SV_PrimitiveID) { + HSPatchConstants c; + c.Edges[0] = 2.0; + c.Edges[1] = 2.0; + c.Edges[2] = 2.0; + c.Edges[3] = 2.0; + c.Inside[0] = 2.0; + c.Inside[1] = 2.0; + return c; +} + +[domain("quad")] +[partitioning("integer")] +[outputtopology("triangle_ccw")] +[outputcontrolpoints(4)] +[patchconstantfunc("PatchConstants")] +HSOutput main(InputPatch patch, uint i : SV_OutputControlPointID) { + HSOutput o; + o.position = patch[i].position; + return o; +} + +#--- domain.hlsl +struct DSInput { + float4 position : POSITION; +}; + +struct DSPatchConstants { + float Edges[4] : SV_TessFactor; + float Inside[2] : SV_InsideTessFactor; +}; + +struct DSOutput { + float4 position : SV_POSITION; + // SV_DomainLocation forwarded for per-pixel capture (interpolated), and the + // DS-stage SV_PrimitiveID forwarded flat (constant over the patch). + float2 uv : TEXCOORD0; + nointerpolation uint primID : PRIMID; +}; + +// Bilinearly interpolate the four corner control points by the (u, v) location. +// The generated triangles tile the input quad, so each patch fully covers its +// half of the viewport. +[domain("quad")] +DSOutput main(DSPatchConstants constants, float2 uv : SV_DomainLocation, + const OutputPatch patch, + uint primID : SV_PrimitiveID) { + float4 bottom = lerp(patch[0].position, patch[1].position, uv.x); + float4 top = lerp(patch[3].position, patch[2].position, uv.x); + + DSOutput o; + o.position = lerp(bottom, top, uv.y); + o.uv = uv; + o.primID = primID; + return o; +} + +#--- pixel.hlsl +struct PSInput { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + nointerpolation uint primID : PRIMID; +}; + +struct Record { + uint PrimID; + float DomU; + float DomV; + float PosX; + float PosY; + float PosZ; + float PosW; +}; + +RWStructuredBuffer Output : register(u0); + +float4 main(PSInput input) : SV_TARGET { + // 4x2 target, top-left origin, row-major. + uint idx = (uint)input.position.y * 4u + (uint)input.position.x; + + Record r; + r.PrimID = input.primID; + r.DomU = input.uv.x; + r.DomV = input.uv.y; + r.PosX = input.position.x; + r.PosY = input.position.y; + r.PosZ = input.position.z; + r.PosW = input.position.w; + Output[idx] = r; + + return float4(1.0, 0.0, 0.0, 1.0); +} + +#--- pipeline.yaml +--- +Shaders: + - Stage: Vertex + Entry: main + - Stage: Hull + Entry: main + - Stage: Domain + Entry: main + - Stage: Pixel + Entry: main +Buffers: + # Two quad patches (cp order: BL, BR, TR, TL), each covering half the viewport. + - Name: VertexData + Format: Float32 + Stride: 8 # float2 position + Data: [ # patch 0: NDC x in [-1, 0] + -1.0, -1.0, # cp0 bottom-left + 0.0, -1.0, # cp1 bottom-right + 0.0, 1.0, # cp2 top-right + -1.0, 1.0, # cp3 top-left + # patch 1: NDC x in [0, 1] + 0.0, -1.0, # cp0 bottom-left + 1.0, -1.0, # cp1 bottom-right + 1.0, 1.0, # cp2 top-right + 0.0, 1.0 ] # cp3 top-left + - Name: RenderTarget + Format: Float32 + Channels: 4 + FillSize: 128 # 4x2 @ 16 bytes per pixel + OutputProps: + Height: 2 + Width: 4 + Depth: 1 + - Name: ResultBuffer + Format: Hex32 + Stride: 28 # sizeof(Record): 1 uint + 6 float + FillSize: 224 # 8 records * 28 bytes + FillValue: 0 + - Name: ResultBuffer_Expected + Format: Hex32 + Stride: 28 + # Per-pixel expected records: PrimID, DomU, DomV, PosX, PosY, PosZ, PosW + # Float bits: 0.0->0x0 0.25->0x3E800000 0.5->0x3F000000 0.75->0x3F400000 + # 1.0->0x3F800000 1.5->0x3FC00000 2.5->0x40200000 3.5->0x40600000 + Data: [ + # --- row 0 (top, NDC +y, DomV = 0.75) --- + # idx0: col0 -> patch0, (u,v)=(0.25,0.75), pos=(0.5,0.5,0,1) + 0x0, 0x3E800000, 0x3F400000, 0x3F000000, 0x3F000000, 0x0, 0x3F800000, + # idx1: col1 -> patch0, (u,v)=(0.75,0.75), pos=(1.5,0.5,0,1) + 0x0, 0x3F400000, 0x3F400000, 0x3FC00000, 0x3F000000, 0x0, 0x3F800000, + # idx2: col2 -> patch1, (u,v)=(0.25,0.75), pos=(2.5,0.5,0,1) + 0x1, 0x3E800000, 0x3F400000, 0x40200000, 0x3F000000, 0x0, 0x3F800000, + # idx3: col3 -> patch1, (u,v)=(0.75,0.75), pos=(3.5,0.5,0,1) + 0x1, 0x3F400000, 0x3F400000, 0x40600000, 0x3F000000, 0x0, 0x3F800000, + # --- row 1 (bottom, NDC -y, DomV = 0.25) --- + # idx4: col0 -> patch0, (u,v)=(0.25,0.25), pos=(0.5,1.5,0,1) + 0x0, 0x3E800000, 0x3E800000, 0x3F000000, 0x3FC00000, 0x0, 0x3F800000, + # idx5: col1 -> patch0, (u,v)=(0.75,0.25), pos=(1.5,1.5,0,1) + 0x0, 0x3F400000, 0x3E800000, 0x3FC00000, 0x3FC00000, 0x0, 0x3F800000, + # idx6: col2 -> patch1, (u,v)=(0.25,0.25), pos=(2.5,1.5,0,1) + 0x1, 0x3E800000, 0x3E800000, 0x40200000, 0x3FC00000, 0x0, 0x3F800000, + # idx7: col3 -> patch1, (u,v)=(0.75,0.25), pos=(3.5,1.5,0,1) + 0x1, 0x3F400000, 0x3E800000, 0x40600000, 0x3FC00000, 0x0, 0x3F800000, + ] +Bindings: + VertexBuffer: VertexData + VertexAttributes: + - Format: Float32 + Channels: 2 + Offset: 0 + Name: POSITION + Topology: PatchList + PatchControlPoints: 4 + RenderTarget: RenderTarget +DescriptorSets: + - Resources: + - Name: ResultBuffer + Kind: RWStructuredBuffer + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 0 +Results: + - Result: SystemValues + Rule: BufferExact + Actual: ResultBuffer + Expected: ResultBuffer_Expected +... +#--- end + +# Metal has tessellation but no Hull/Domain stages: HS is a compute kernel +# writing per-patch factors, DS is a post-tessellation vertex function tagged +# [[patch(...)]]. Mapping HLSL HS/DS onto that isn't wired up, so skip Metal. +# UNSUPPORTED: Metal + +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target -T vs_6_0 -Fo %t-vertex.o %t/vertex.hlsl +# RUN: %dxc_target -T hs_6_0 -Fo %t-hull.o %t/hull.hlsl +# RUN: %dxc_target -T ds_6_0 -Fo %t-domain.o %t/domain.hlsl +# RUN: %dxc_target -T ps_6_0 -Fo %t-pixel.o %t/pixel.hlsl +# RUN: %offloader %t/pipeline.yaml %t-vertex.o %t-hull.o %t-domain.o %t-pixel.o diff --git a/test/Feature/Semantics/HullSystemValues.test b/test/Feature/Semantics/HullSystemValues.test new file mode 100644 index 000000000..6c5a264ee --- /dev/null +++ b/test/Feature/Semantics/HullSystemValues.test @@ -0,0 +1,254 @@ +# Bundled hull-shader (HS) system-value test. Exercises: +# * SV_PrimitiveID - HS input, in BOTH the patch-constant function and +# the main control-point function (must agree). +# primID in HS main is new coverage. +# * SV_OutputControlPointID - HS main input; each of the 3 invocations must see +# its own index 0,1,2. +# * SV_TessFactor / SV_InsideTessFactor - set in HS, read back in DS (round-trip). +# +# SV_PrimitiveID: https://github.com/llvm/wg-hlsl/issues/160 +# SV_OutputControlPointID / SV_TessFactor: https://github.com/llvm/wg-hlsl/issues/141 +# Clang's HLSL -> DXIL lowering does not yet implement the tessellation stages, +# so this is XFAIL on Clang. Metal has no Hull/Domain stages (see note below). +# +# The HS system values cannot be read back directly, so they are smuggled +# downstream: HS main stashes (SV_PrimitiveID, SV_OutputControlPointID) into each +# output control point; the patch-constant function stashes its SV_PrimitiveID +# into a user patch-constant field. The DS reads the whole OutputPatch plus the +# patch constants and forwards everything (flat) to the PS, which records it. +# +# Geometry: two independent triangle patches (6 control points, +# PatchControlPoints = 3), tess factor 1 (no subdivision -> each patch emits its +# original triangle). The input assembler hands each patch a distinct +# SV_PrimitiveID (0, 1). Each triangle is a tall sliver that covers exactly one +# pixel center of a 2x1 render target: +# +# patch 0 -> pixel 0 (NDC x center -0.5) -> idx 0 +# patch 1 -> pixel 1 (NDC x center +0.5) -> idx 1 + +#--- vertex.hlsl +struct VSOutput { + float4 position : POSITION; +}; + +VSOutput main(float4 position : POSITION) { + VSOutput o; + o.position = position; + return o; +} + +#--- hull.hlsl +struct HSInput { + float4 position : POSITION; +}; + +struct HSOutput { + float4 position : POSITION; + // SV_PrimitiveID and SV_OutputControlPointID observed by this HS invocation. + uint hsPrimID : HSPRIMID; + uint hsCpid : HSCPID; +}; + +struct HSPatchConstants { + float Edges[3] : SV_TessFactor; + float Inside : SV_InsideTessFactor; + uint pcPrimID : PCPRIMID; // SV_PrimitiveID observed in the patch-constant func +}; + +HSPatchConstants PatchConstants(InputPatch patch, + uint patchID : SV_PrimitiveID) { + HSPatchConstants c; + c.Edges[0] = 1.0; + c.Edges[1] = 1.0; + c.Edges[2] = 1.0; + c.Inside = 1.0; + c.pcPrimID = patchID; + return c; +} + +[domain("tri")] +[partitioning("integer")] +[outputtopology("triangle_cw")] +[outputcontrolpoints(3)] +[patchconstantfunc("PatchConstants")] +HSOutput main(InputPatch patch, + uint i : SV_OutputControlPointID, + uint patchID : SV_PrimitiveID) { + HSOutput o; + o.position = patch[i].position; + o.hsPrimID = patchID; + o.hsCpid = i; + return o; +} + +#--- domain.hlsl +struct DSInput { + float4 position : POSITION; + uint hsPrimID : HSPRIMID; + uint hsCpid : HSCPID; +}; + +struct DSPatchConstants { + float Edges[3] : SV_TessFactor; + float Inside : SV_InsideTessFactor; + uint pcPrimID : PCPRIMID; +}; + +struct DSOutput { + float4 position : SV_POSITION; + nointerpolation uint pcPrimID : PCPRIMID; + nointerpolation uint hsMainPrimID : HSPRIMID; + nointerpolation uint cpid0 : CPID0; + nointerpolation uint cpid1 : CPID1; + nointerpolation uint cpid2 : CPID2; + nointerpolation float edgeFactor : EDGEF; + nointerpolation float insideFactor : INSIDEF; +}; + +// Factor-1 tri tessellation reproduces the control triangle exactly (the three +// generated vertices sit at barycentric (1,0,0),(0,1,0),(0,0,1)). +[domain("tri")] +DSOutput main(DSPatchConstants constants, float3 bary : SV_DomainLocation, + const OutputPatch patch) { + DSOutput o; + o.position = bary.x * patch[0].position + bary.y * patch[1].position + + bary.z * patch[2].position; + o.pcPrimID = constants.pcPrimID; + o.hsMainPrimID = patch[0].hsPrimID; // identical across control points + o.cpid0 = patch[0].hsCpid; + o.cpid1 = patch[1].hsCpid; + o.cpid2 = patch[2].hsCpid; + o.edgeFactor = constants.Edges[0]; + o.insideFactor = constants.Inside; + return o; +} + +#--- pixel.hlsl +struct PSInput { + float4 position : SV_POSITION; + nointerpolation uint pcPrimID : PCPRIMID; + nointerpolation uint hsMainPrimID : HSPRIMID; + nointerpolation uint cpid0 : CPID0; + nointerpolation uint cpid1 : CPID1; + nointerpolation uint cpid2 : CPID2; + nointerpolation float edgeFactor : EDGEF; + nointerpolation float insideFactor : INSIDEF; +}; + +struct Record { + uint PCPrimID; + uint HSMainPrimID; + uint Cpid0; + uint Cpid1; + uint Cpid2; + float EdgeFactor; + float InsideFactor; +}; + +RWStructuredBuffer Output : register(u0); + +float4 main(PSInput input) : SV_TARGET { + uint idx = (uint)input.position.x; // 2x1 target, height 1 + + Record r; + r.PCPrimID = input.pcPrimID; + r.HSMainPrimID = input.hsMainPrimID; + r.Cpid0 = input.cpid0; + r.Cpid1 = input.cpid1; + r.Cpid2 = input.cpid2; + r.EdgeFactor = input.edgeFactor; + r.InsideFactor = input.insideFactor; + Output[idx] = r; + + return float4(1.0, 0.0, 0.0, 1.0); +} + +#--- pipeline.yaml +--- +Shaders: + - Stage: Vertex + Entry: main + - Stage: Hull + Entry: main + - Stage: Domain + Entry: main + - Stage: Pixel + Entry: main +Buffers: + # Two triangle patches; each a tall sliver around one pixel center. + - Name: VertexData + Format: Float32 + Stride: 8 # float2 position + Data: [ # patch 0 -> pixel 0 (center NDC x = -0.5) + -0.9, -0.9, + -0.1, -0.9, + -0.5, 0.9, + # patch 1 -> pixel 1 (center NDC x = +0.5) + 0.1, -0.9, + 0.9, -0.9, + 0.5, 0.9 ] + - Name: RenderTarget + Format: Float32 + Channels: 4 + FillSize: 32 # 2x1 @ 16 bytes per pixel + OutputProps: + Height: 1 + Width: 2 + Depth: 1 + - Name: ResultBuffer + Format: Hex32 + Stride: 28 # sizeof(Record): 5 uint + 2 float + FillSize: 56 # 2 records * 28 bytes + FillValue: 0 + - Name: ResultBuffer_Expected + Format: Hex32 + Stride: 28 + # Per-pixel expected records: + # PCPrimID, HSMainPrimID, Cpid0, Cpid1, Cpid2, EdgeFactor, InsideFactor + # 1.0 -> 0x3F800000 + Data: [ + # idx0: patch 0 + 0x0, 0x0, 0x0, 0x1, 0x2, 0x3F800000, 0x3F800000, + # idx1: patch 1 + 0x1, 0x1, 0x0, 0x1, 0x2, 0x3F800000, 0x3F800000, + ] +Bindings: + VertexBuffer: VertexData + VertexAttributes: + - Format: Float32 + Channels: 2 + Offset: 0 + Name: POSITION + Topology: PatchList + PatchControlPoints: 3 + RenderTarget: RenderTarget +DescriptorSets: + - Resources: + - Name: ResultBuffer + Kind: RWStructuredBuffer + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 0 +Results: + - Result: SystemValues + Rule: BufferExact + Actual: ResultBuffer + Expected: ResultBuffer_Expected +... +#--- end + +# Metal has tessellation but no Hull/Domain stages: HS is a compute kernel +# writing per-patch factors, DS is a post-tessellation vertex function tagged +# [[patch(...)]]. Mapping HLSL HS/DS onto that isn't wired up, so skip Metal. +# UNSUPPORTED: Metal + +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target -T vs_6_0 -Fo %t-vertex.o %t/vertex.hlsl +# RUN: %dxc_target -T hs_6_0 -Fo %t-hull.o %t/hull.hlsl +# RUN: %dxc_target -T ds_6_0 -Fo %t-domain.o %t/domain.hlsl +# RUN: %dxc_target -T ps_6_0 -Fo %t-pixel.o %t/pixel.hlsl +# RUN: %offloader %t/pipeline.yaml %t-vertex.o %t-hull.o %t-domain.o %t-pixel.o From 07ff855b1e66193feb15875ed5dcb87f7957341a Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Thu, 11 Jun 2026 10:08:43 +0200 Subject: [PATCH 2/3] fmt --- .../Feature/Semantics/DomainSystemValues.test | 23 ++++--------- test/Feature/Semantics/HullSystemValues.test | 34 ++++++++----------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/test/Feature/Semantics/DomainSystemValues.test b/test/Feature/Semantics/DomainSystemValues.test index 2f7b138e4..830038909 100644 --- a/test/Feature/Semantics/DomainSystemValues.test +++ b/test/Feature/Semantics/DomainSystemValues.test @@ -3,11 +3,6 @@ # * SV_DomainLocation - DS input (the tessellator's (u, v) for this vertex) # * SV_Position - DS output -> rasterizer -> PS input (pixel center) # -# SV_PrimitiveID: https://github.com/llvm/wg-hlsl/issues/160 -# SV_DomainLocation / SV_Position (DS): https://github.com/llvm/wg-hlsl/issues/141 -# Clang's HLSL -> DXIL lowering does not yet implement the tessellation stages, -# so this is XFAIL on Clang. Metal has no Hull/Domain stages (see note below). -# # Geometry: two independent quad patches drawn back-to-back (8 control points, # PatchControlPoints = 4). The input-assembler hands each patch a distinct # SV_PrimitiveID (0, 1). Each patch covers one half of a 4x2 render target: @@ -119,7 +114,7 @@ struct PSInput { }; struct Record { - uint PrimID; + uint PrimID; float DomU; float DomV; float PosX; @@ -131,17 +126,16 @@ struct Record { RWStructuredBuffer Output : register(u0); float4 main(PSInput input) : SV_TARGET { - // 4x2 target, top-left origin, row-major. uint idx = (uint)input.position.y * 4u + (uint)input.position.x; Record r; r.PrimID = input.primID; - r.DomU = input.uv.x; - r.DomV = input.uv.y; - r.PosX = input.position.x; - r.PosY = input.position.y; - r.PosZ = input.position.z; - r.PosW = input.position.w; + r.DomU = input.uv.x; + r.DomV = input.uv.y; + r.PosX = input.position.x; + r.PosY = input.position.y; + r.PosZ = input.position.z; + r.PosW = input.position.w; Output[idx] = r; return float4(1.0, 0.0, 0.0, 1.0); @@ -239,9 +233,6 @@ Results: ... #--- end -# Metal has tessellation but no Hull/Domain stages: HS is a compute kernel -# writing per-patch factors, DS is a post-tessellation vertex function tagged -# [[patch(...)]]. Mapping HLSL HS/DS onto that isn't wired up, so skip Metal. # UNSUPPORTED: Metal # XFAIL: Clang diff --git a/test/Feature/Semantics/HullSystemValues.test b/test/Feature/Semantics/HullSystemValues.test index 6c5a264ee..3485aa703 100644 --- a/test/Feature/Semantics/HullSystemValues.test +++ b/test/Feature/Semantics/HullSystemValues.test @@ -1,15 +1,12 @@ # Bundled hull-shader (HS) system-value test. Exercises: # * SV_PrimitiveID - HS input, in BOTH the patch-constant function and # the main control-point function (must agree). -# primID in HS main is new coverage. # * SV_OutputControlPointID - HS main input; each of the 3 invocations must see # its own index 0,1,2. # * SV_TessFactor / SV_InsideTessFactor - set in HS, read back in DS (round-trip). # # SV_PrimitiveID: https://github.com/llvm/wg-hlsl/issues/160 # SV_OutputControlPointID / SV_TessFactor: https://github.com/llvm/wg-hlsl/issues/141 -# Clang's HLSL -> DXIL lowering does not yet implement the tessellation stages, -# so this is XFAIL on Clang. Metal has no Hull/Domain stages (see note below). # # The HS system values cannot be read back directly, so they are smuggled # downstream: HS main stashes (SV_PrimitiveID, SV_OutputControlPointID) into each @@ -44,7 +41,6 @@ struct HSInput { struct HSOutput { float4 position : POSITION; - // SV_PrimitiveID and SV_OutputControlPointID observed by this HS invocation. uint hsPrimID : HSPRIMID; uint hsCpid : HSCPID; }; @@ -52,7 +48,8 @@ struct HSOutput { struct HSPatchConstants { float Edges[3] : SV_TessFactor; float Inside : SV_InsideTessFactor; - uint pcPrimID : PCPRIMID; // SV_PrimitiveID observed in the patch-constant func + // SV_PrimitiveID observed in the patch-constant function + uint pcPrimID : PCPRIMID; }; HSPatchConstants PatchConstants(InputPatch patch, @@ -71,8 +68,7 @@ HSPatchConstants PatchConstants(InputPatch patch, [outputtopology("triangle_cw")] [outputcontrolpoints(3)] [patchconstantfunc("PatchConstants")] -HSOutput main(InputPatch patch, - uint i : SV_OutputControlPointID, +HSOutput main(InputPatch patch, uint i : SV_OutputControlPointID, uint patchID : SV_PrimitiveID) { HSOutput o; o.position = patch[i].position; @@ -114,7 +110,7 @@ DSOutput main(DSPatchConstants constants, float3 bary : SV_DomainLocation, o.position = bary.x * patch[0].position + bary.y * patch[1].position + bary.z * patch[2].position; o.pcPrimID = constants.pcPrimID; - o.hsMainPrimID = patch[0].hsPrimID; // identical across control points + o.hsMainPrimID = patch[0].hsPrimID; o.cpid0 = patch[0].hsCpid; o.cpid1 = patch[1].hsCpid; o.cpid2 = patch[2].hsCpid; @@ -136,11 +132,11 @@ struct PSInput { }; struct Record { - uint PCPrimID; - uint HSMainPrimID; - uint Cpid0; - uint Cpid1; - uint Cpid2; + uint PCPrimID; + uint HSMainPrimID; + uint Cpid0; + uint Cpid1; + uint Cpid2; float EdgeFactor; float InsideFactor; }; @@ -148,15 +144,15 @@ struct Record { RWStructuredBuffer Output : register(u0); float4 main(PSInput input) : SV_TARGET { - uint idx = (uint)input.position.x; // 2x1 target, height 1 + uint idx = (uint)input.position.x; Record r; - r.PCPrimID = input.pcPrimID; + r.PCPrimID = input.pcPrimID; r.HSMainPrimID = input.hsMainPrimID; - r.Cpid0 = input.cpid0; - r.Cpid1 = input.cpid1; - r.Cpid2 = input.cpid2; - r.EdgeFactor = input.edgeFactor; + r.Cpid0 = input.cpid0; + r.Cpid1 = input.cpid1; + r.Cpid2 = input.cpid2; + r.EdgeFactor = input.edgeFactor; r.InsideFactor = input.insideFactor; Output[idx] = r; From 6cdafb02f53c6d855804e612666c3974d8451826 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Mon, 15 Jun 2026 10:45:33 +0200 Subject: [PATCH 3/3] PR comments --- test/Feature/Semantics/DomainSystemValues.test | 7 ++++++- test/Feature/Semantics/HullSystemValues.test | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/test/Feature/Semantics/DomainSystemValues.test b/test/Feature/Semantics/DomainSystemValues.test index 830038909..296ff1fd2 100644 --- a/test/Feature/Semantics/DomainSystemValues.test +++ b/test/Feature/Semantics/DomainSystemValues.test @@ -10,7 +10,7 @@ # patch 0 -> NDC x in [-1, 0] -> columns 0,1 # patch 1 -> NDC x in [ 0, 1] -> columns 2,3 # -# With integer partitioning and all tess factors = 2, each patch is subdivided +# With integer partitioning and all tessellation factors = 2, each patch is subdivided # into a 2x2 grid of sub-quads. The sub-quad seams fall at domain (u, v) = 0.5, # i.e. exactly on a pixel *boundary*, so every pixel center maps to a clean # domain coordinate of 0.25 or 0.75 on each axis -- identical to the mapping @@ -182,6 +182,8 @@ Buffers: FillValue: 0 - Name: ResultBuffer_Expected Format: Hex32 + # Each record mixes a uint (PrimID) with floats, and the YAML buffer format + # is uniform per buffer, so the values are written as raw 32-bit hex. Stride: 28 # Per-pixel expected records: PrimID, DomU, DomV, PosX, PosY, PosZ, PosW # Float bits: 0.0->0x0 0.25->0x3E800000 0.5->0x3F000000 0.75->0x3F400000 @@ -233,6 +235,9 @@ Results: ... #--- end +# Metal has tessellation but no Hull/Domain stages: HS is a compute kernel +# writing per-patch factors, DS is a post-tessellation vertex function tagged +# [[patch(...)]]. Mapping HLSL HS/DS onto that isn't wired up, so skip Metal. # UNSUPPORTED: Metal # XFAIL: Clang diff --git a/test/Feature/Semantics/HullSystemValues.test b/test/Feature/Semantics/HullSystemValues.test index 3485aa703..64a26013a 100644 --- a/test/Feature/Semantics/HullSystemValues.test +++ b/test/Feature/Semantics/HullSystemValues.test @@ -15,7 +15,7 @@ # patch constants and forwards everything (flat) to the PS, which records it. # # Geometry: two independent triangle patches (6 control points, -# PatchControlPoints = 3), tess factor 1 (no subdivision -> each patch emits its +# PatchControlPoints = 3), tessellation factor 1 (no subdivision -> each patch emits its # original triangle). The input assembler hands each patch a distinct # SV_PrimitiveID (0, 1). Each triangle is a tall sliver that covers exactly one # pixel center of a 2x1 render target: