Skip to content

Commit 866edb0

Browse files
MarijnS95claude
authored andcommitted
Cover PSO-only RT surface: recursion, skip-CH flag, callable
Three tests stacked on #1275 covering features that inline RT (RayQuery in compute) physically can't express — they're only reachable through a DispatchRays-driven RT pipeline. - `trace-ray-recursion.test` — closest-hit fires a secondary TraceRay from an above-the-triangle origin. First-level CH sees payload=0 → bumps to 0x1 → calls TraceRay. Second-level CH sees payload!=0 → writes 0x10. Unwinds: first-level OR's in 0x100. Final payload 0x110 (272 decimal). `RayTracingPipelineConfig.MaxTraceRecursion- Depth: 2` so both TraceRay calls are within budget. - `ray-flag-skip-closest-hit.test` — two lanes fire identical rays at the same triangle. Lane 0 uses RAY_FLAG_NONE so CH runs and writes 0xBEEF. Lane 1 uses RAY_FLAG_SKIP_CLOSEST_HIT_SHADER (PSO-only — inline RT has no equivalent) so CH is skipped and payload keeps its initial 0xAAAA. Output [0xBEEF, 0xAAAA]. - `callable-shader.test` — two callable shaders writing distinct sentinels (0xAAAA / 0xBBBB). Each lane calls `CallShader(Idx, ...)` so the SBT callable region's per-record routing is exercised independently of the hit-group / miss routing already covered in #1277. Callable shaders themselves don't exist in inline RT. This test stays `# XFAIL: Clang, Vulkan` because DXC's `-spirv` backend lists every callable's IncomingCallableDataKHR variable in every callable entry point's interface, violating VUID-Standalone- Spirv-IncomingCallableDataKHR-04706 and getting rejected by vk- CreateShaderModule. The framework's Vulkan SBT / callable path is correct — running `spirv-opt --remove-unused-interface-variables` on the DXC output cleans the SPIR-V and the test passes natively. Track upstream. All three pass on Metal once the bring-up PR ahead of this commit sets the raygen pipeline's `setMaxCallStackDepth(MaxTraceRecursionDepth)` so nested TraceRay actually unwinds (with the default of 1, the second TraceRay was silently dropped and the recursion test produced 0x1 instead of 0x110). Locally verified on the user's Linux box: - Vulkan via the native offloader: recursion + skip-CH PASS; callable PASSes after spirv-opt cleanup (XFAILs from raw DXC SPIR-V as documented above). - D3D12 via Wine + vkd3d-proton + cross-compiled offloader.exe: all three PASS. And on macOS 15 / metal-irconverter 3.1.1 via the native offloader: - All three PASS (recursion + skip-CH + callable). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f4e85ca commit 866edb0

3 files changed

Lines changed: 354 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#--- source.hlsl
2+
3+
// Callable shaders receive an inout user-defined parameter — Vulkan / DXR
4+
// don't have a Built-in convention beyond "size matches what the caller
5+
// declared". One uint of payload is enough to confirm routing.
6+
struct CallableParams {
7+
uint Value;
8+
};
9+
10+
[[vk::binding(0, 0)]] RWStructuredBuffer<uint> Output : register(u0);
11+
12+
[shader("raygeneration")]
13+
void RayGen() {
14+
// Each lane calls a different callable shader index. CallShader picks
15+
// the callable record at the given index in the SBT's Callable region
16+
// — inline RT has no equivalent of this stage.
17+
const uint Idx = DispatchRaysIndex().x;
18+
CallableParams P;
19+
P.Value = 0;
20+
CallShader(Idx, P);
21+
Output[Idx] = P.Value;
22+
}
23+
24+
[shader("callable")]
25+
void CallableA(inout CallableParams P) {
26+
P.Value = 0xAAAA;
27+
}
28+
29+
[shader("callable")]
30+
void CallableB(inout CallableParams P) {
31+
P.Value = 0xBBBB;
32+
}
33+
//--- pipeline.yaml
34+
---
35+
Shaders:
36+
- Stage: RayGeneration
37+
Entry: RayGen
38+
- Stage: Callable
39+
Entry: CallableA
40+
- Stage: Callable
41+
Entry: CallableB
42+
Buffers:
43+
- Name: Output
44+
Format: UInt32
45+
Stride: 4
46+
FillSize: 8
47+
- Name: Expected
48+
Format: UInt32
49+
Stride: 4
50+
Data: [ 0xAAAA, 0xBBBB ]
51+
RayTracingPipelineConfig:
52+
# No TraceRay calls — depth 1 is sufficient (CallShader doesn't count
53+
# against ray recursion).
54+
MaxTraceRecursionDepth: 1
55+
MaxPayloadSizeInBytes: 4
56+
ShaderBindingTable:
57+
RayGen:
58+
ShaderName: RayGen
59+
Callable:
60+
- ShaderName: CallableA
61+
- ShaderName: CallableB
62+
DescriptorSets:
63+
- Resources:
64+
- Name: Output
65+
Kind: RWStructuredBuffer
66+
DirectXBinding:
67+
Register: 0
68+
Space: 0
69+
VulkanBinding:
70+
Binding: 0
71+
DispatchParameters:
72+
DispatchGroupCount: [ 2, 1, 1 ]
73+
Results:
74+
- Result: CallableRouting
75+
Rule: BufferExact
76+
Actual: Output
77+
Expected: Expected
78+
...
79+
#--- end
80+
81+
# REQUIRES: raytracing-pipeline
82+
# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268
83+
# DXC's `-spirv` backend lists *every* callable's IncomingCallableDataKHR
84+
# variable in *every* callable entry point's interface, which violates
85+
# VUID-StandaloneSpirv-IncomingCallableDataKHR-04706 and gets rejected by
86+
# vkCreateShaderModule. The Vulkan backend implementation is correct
87+
# (`spirv-opt --remove-unused-interface-variables` cleans the SPIR-V and
88+
# the test then passes natively) — track the DXC fix upstream.
89+
# XFAIL: Clang, Vulkan
90+
91+
# RUN: split-file %s %t
92+
# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl
93+
# RUN: %offloader %t/pipeline.yaml %t.o
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#--- source.hlsl
2+
3+
struct Payload {
4+
uint Value;
5+
};
6+
7+
[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0);
8+
[[vk::binding(1, 0)]] RWStructuredBuffer<uint> Output : register(u0);
9+
10+
[shader("raygeneration")]
11+
void RayGen() {
12+
// Both lanes fire identical rays that hit the same triangle. Lane 0
13+
// uses RAY_FLAG_NONE so the closest-hit shader runs and writes
14+
// 0xBEEF into the payload. Lane 1 sets
15+
// RAY_FLAG_SKIP_CLOSEST_HIT_SHADER (a PSO-only flag — inline RT can't
16+
// express this) so the traversal records a hit but the closest-hit
17+
// shader is never invoked. With initial payload = 0xAAAA the output
18+
// is the sentinel for lane 1 and 0xBEEF for lane 0.
19+
const uint Idx = DispatchRaysIndex().x;
20+
const uint Flags =
21+
Idx == 0 ? RAY_FLAG_NONE : RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
22+
23+
Payload P;
24+
P.Value = 0xAAAA;
25+
RayDesc Ray;
26+
Ray.Origin = float3(0, 0, 1);
27+
Ray.Direction = float3(0, 0, -1);
28+
Ray.TMin = 0.0;
29+
Ray.TMax = 100.0;
30+
TraceRay(Scene, Flags, 0xFF, 0, 1, 0, Ray, P);
31+
Output[Idx] = P.Value;
32+
}
33+
34+
[shader("miss")]
35+
void MissMain(inout Payload P) {
36+
// Sentinel — every ray hits the triangle, so this never runs.
37+
P.Value = 0xDEAD;
38+
}
39+
40+
[shader("closesthit")]
41+
void ClosestHitMain(inout Payload P,
42+
in BuiltInTriangleIntersectionAttributes Attr) {
43+
P.Value = 0xBEEF;
44+
}
45+
//--- pipeline.yaml
46+
---
47+
Shaders:
48+
- Stage: RayGeneration
49+
Entry: RayGen
50+
- Stage: Miss
51+
Entry: MissMain
52+
- Stage: ClosestHit
53+
Entry: ClosestHitMain
54+
Buffers:
55+
- Name: Vertices
56+
Format: Float32
57+
Stride: 12
58+
Data: [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]
59+
- Name: Output
60+
Format: UInt32
61+
Stride: 4
62+
FillSize: 8
63+
- Name: Expected
64+
Format: UInt32
65+
Stride: 4
66+
# Lane 0: closest-hit runs, writes 0xBEEF.
67+
# Lane 1: closest-hit skipped, payload stays at the initial 0xAAAA.
68+
Data: [ 0xBEEF, 0xAAAA ]
69+
AccelerationStructures:
70+
BLAS:
71+
- Name: TriangleBLAS
72+
Triangles:
73+
- VertexBuffer: Vertices
74+
VertexFormat: RGB32Float
75+
VertexStride: 12
76+
VertexCount: 3
77+
TLAS:
78+
- Name: Scene
79+
Instances:
80+
- BLAS: TriangleBLAS
81+
RayTracingPipelineConfig:
82+
MaxTraceRecursionDepth: 1
83+
MaxPayloadSizeInBytes: 4
84+
HitGroups:
85+
- Name: TriangleHitGroup
86+
Type: Triangles
87+
ClosestHit: ClosestHitMain
88+
ShaderBindingTable:
89+
RayGen:
90+
ShaderName: RayGen
91+
Miss:
92+
- ShaderName: MissMain
93+
HitGroup:
94+
- ShaderName: TriangleHitGroup
95+
DescriptorSets:
96+
- Resources:
97+
- Name: Scene
98+
Kind: AccelerationStructure
99+
DirectXBinding:
100+
Register: 0
101+
Space: 0
102+
VulkanBinding:
103+
Binding: 0
104+
- Name: Output
105+
Kind: RWStructuredBuffer
106+
DirectXBinding:
107+
Register: 0
108+
Space: 0
109+
VulkanBinding:
110+
Binding: 1
111+
DispatchParameters:
112+
DispatchGroupCount: [ 2, 1, 1 ]
113+
Results:
114+
- Result: RayFlagSkipClosestHit
115+
Rule: BufferExact
116+
Actual: Output
117+
Expected: Expected
118+
...
119+
#--- end
120+
121+
# REQUIRES: raytracing-pipeline
122+
# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268
123+
# XFAIL: Clang
124+
125+
# RUN: split-file %s %t
126+
# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl
127+
# RUN: %offloader %t/pipeline.yaml %t.o
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#--- source.hlsl
2+
3+
struct Payload {
4+
uint Value;
5+
};
6+
7+
[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0);
8+
[[vk::binding(1, 0)]] RWStructuredBuffer<uint> Output : register(u0);
9+
10+
[shader("raygeneration")]
11+
void RayGen() {
12+
Payload P;
13+
P.Value = 0;
14+
RayDesc Ray;
15+
Ray.Origin = float3(0, 0, 1);
16+
Ray.Direction = float3(0, 0, -1);
17+
Ray.TMin = 0.0;
18+
Ray.TMax = 100.0;
19+
TraceRay(Scene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, Ray, P);
20+
Output[0] = P.Value;
21+
}
22+
23+
[shader("miss")]
24+
void MissMain(inout Payload P) {
25+
// Sentinel — never observed (every ray hits the triangle).
26+
P.Value = 0xDEAD;
27+
}
28+
29+
[shader("closesthit")]
30+
void RecursiveCH(inout Payload P, in BuiltInTriangleIntersectionAttributes Attr) {
31+
// First entry into the hit shader: payload still carries raygen's 0.
32+
// Bump it to a marker, fire a secondary ray from a fresh origin above the
33+
// triangle so we hit the same triangle again, then OR in the high marker
34+
// when the secondary chain unwinds. The secondary invocation sees a
35+
// non-zero payload and goes down the second-level branch.
36+
if (P.Value == 0) {
37+
P.Value = 0x1;
38+
RayDesc Ray;
39+
Ray.Origin = float3(0, 0, 2);
40+
Ray.Direction = float3(0, 0, -1);
41+
Ray.TMin = 0.0;
42+
Ray.TMax = 100.0;
43+
TraceRay(Scene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, Ray, P);
44+
P.Value |= 0x100;
45+
} else {
46+
// Second-level hit: write the low marker and don't recurse further.
47+
P.Value = 0x10;
48+
}
49+
}
50+
//--- pipeline.yaml
51+
---
52+
Shaders:
53+
- Stage: RayGeneration
54+
Entry: RayGen
55+
- Stage: Miss
56+
Entry: MissMain
57+
- Stage: ClosestHit
58+
Entry: RecursiveCH
59+
Buffers:
60+
- Name: Vertices
61+
Format: Float32
62+
Stride: 12
63+
Data: [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]
64+
- Name: Output
65+
Format: UInt32
66+
Stride: 4
67+
FillSize: 4
68+
- Name: Expected
69+
Format: UInt32
70+
Stride: 4
71+
# First-level CH sets Value=0x1 (overwritten by secondary CH to 0x10), then
72+
# OR's in 0x100 after the secondary chain unwinds — final = 0x110.
73+
Data: [ 0x110 ]
74+
AccelerationStructures:
75+
BLAS:
76+
- Name: TriangleBLAS
77+
Triangles:
78+
- VertexBuffer: Vertices
79+
VertexFormat: RGB32Float
80+
VertexStride: 12
81+
VertexCount: 3
82+
TLAS:
83+
- Name: Scene
84+
Instances:
85+
- BLAS: TriangleBLAS
86+
RayTracingPipelineConfig:
87+
# Raygen invokes TraceRay (depth 1) and the first-level CH invokes
88+
# TraceRay again (depth 2). Both need to be allowed.
89+
MaxTraceRecursionDepth: 2
90+
MaxPayloadSizeInBytes: 4
91+
HitGroups:
92+
- Name: TriangleHitGroup
93+
Type: Triangles
94+
ClosestHit: RecursiveCH
95+
ShaderBindingTable:
96+
RayGen:
97+
ShaderName: RayGen
98+
Miss:
99+
- ShaderName: MissMain
100+
HitGroup:
101+
- ShaderName: TriangleHitGroup
102+
DescriptorSets:
103+
- Resources:
104+
- Name: Scene
105+
Kind: AccelerationStructure
106+
DirectXBinding:
107+
Register: 0
108+
Space: 0
109+
VulkanBinding:
110+
Binding: 0
111+
- Name: Output
112+
Kind: RWStructuredBuffer
113+
DirectXBinding:
114+
Register: 0
115+
Space: 0
116+
VulkanBinding:
117+
Binding: 1
118+
DispatchParameters:
119+
DispatchGroupCount: [ 1, 1, 1 ]
120+
Results:
121+
- Result: TraceRayRecursion
122+
Rule: BufferExact
123+
Actual: Output
124+
Expected: Expected
125+
...
126+
#--- end
127+
128+
# REQUIRES: raytracing-pipeline
129+
# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268
130+
# XFAIL: Clang
131+
132+
# RUN: split-file %s %t
133+
# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl
134+
# RUN: %offloader %t/pipeline.yaml %t.o

0 commit comments

Comments
 (0)