From 365c67e33cdead15d29294e9740a85650b83adb4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 3 Jun 2026 12:41:18 +0200 Subject: [PATCH] Add first PSO RT test batch: DispatchRays + SBT routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four small tests stacked on top of #1275, each isolating one shader-observable PSO raytracing surface. They follow the same shape as the inline-RT batch already in #1271 / #1272 / #1274 / #1276 — one .test file per behavior, single-purpose shader, exact buffer comparison. - `dispatch-rays-index.test` — 4x1x1 dispatch, raygen writes `DispatchRaysIndex().x` into `Output[index]`. Confirms the dispatch grid plumbs through to the per-lane system value with no BLAS / TLAS / hit groups in play (RT-pipeline-only, no AS binding). - `dispatch-rays-dimensions.test` — 2x3x1 dispatch, raygen packs the constant `DispatchRaysDimensions()` into one uint per lane. Confirms every lane sees the host-side `{W, H, D}` even when only one dimension > 1. - `miss-shader-index.test` — two miss shaders writing distinct sentinels (0xAA / 0xBB). 2-lane dispatch picks `MissShaderIndex` 0 and 1 respectively; rays start far enough from the geometry that every ray misses. Verifies the SBT miss region's per-record routing. - `ray-contribution-to-hit-group-index.test` — two hit groups with distinct closest-hit shaders (0xA1 / 0xB2). 2-lane dispatch picks `RayContributionToHitGroupIndex` 0 and 1, every ray hits the same triangle. Verifies the SBT hit-group region's per-record routing. The first two have no AS / Miss / HitGroup in their pipeline at all — just a raygen + a UAV — which exercises the minimum viable RT pipeline shape (one raygen group, zero-sized miss / hit / callable SBT regions). The latter two reuse the single-triangle BLAS/TLAS from `raygen-roundtrip.test`. All four tests are `# REQUIRES: raytracing-pipeline` with `# XFAIL: Clang` — Clang (`clang-dxc`) doesn't yet lower `[shader("…")]` entry points to either DXIL libraries or SPIR-V. With the Metal RT bring-up rebased on top, all four pass natively on Apple Silicon and Metal is dropped from the XFAIL list. Locally verified end-to-end on the user's Linux box: - Vulkan via the native offloader against an NVIDIA RTX 3060: all four tests PASS. - D3D12 via Wine + vkd3d-proton + the cross-compiled offloader.exe on the same GPU: all four tests PASS. And on macOS 15 / metal-irconverter 3.1.1: - Metal via the native offloader: all four tests PASS. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/Feature/RT/dispatch-rays-dimensions.test | 62 +++++++++ test/Feature/RT/dispatch-rays-index.test | 56 ++++++++ test/Feature/RT/miss-shader-index.test | 127 +++++++++++++++++ .../ray-contribution-to-hit-group-index.test | 130 ++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 test/Feature/RT/dispatch-rays-dimensions.test create mode 100644 test/Feature/RT/dispatch-rays-index.test create mode 100644 test/Feature/RT/miss-shader-index.test create mode 100644 test/Feature/RT/ray-contribution-to-hit-group-index.test diff --git a/test/Feature/RT/dispatch-rays-dimensions.test b/test/Feature/RT/dispatch-rays-dimensions.test new file mode 100644 index 000000000..56e27386b --- /dev/null +++ b/test/Feature/RT/dispatch-rays-dimensions.test @@ -0,0 +1,62 @@ +#--- source.hlsl + +[[vk::binding(0, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + // DispatchRaysDimensions() echoes the host-side {W, H, D}. Every lane sees + // the same constant; write the packed value at the lane's linear index so + // the result is independent of which lane orders its store first. + const uint3 D = DispatchRaysDimensions(); + const uint Idx = DispatchRaysIndex().y * D.x + DispatchRaysIndex().x; + // Pack {W, H} into one uint for an exact comparison; the dispatch is 2D so + // D.z is always 1 (no need to round-trip it through the buffer). + Output[Idx] = (D.x << 16) | (D.y & 0xFFFF); +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen +Buffers: + - Name: Output + Format: UInt32 + Stride: 4 + FillSize: 24 + - Name: Expected + Format: UInt32 + Stride: 4 + # Dispatch is 2x3x1 (W=2, H=3, D=1) so every lane writes (2<<16)|3 = 0x20003. + Data: [ 0x20003, 0x20003, 0x20003, 0x20003, 0x20003, 0x20003 ] +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 0 +ShaderBindingTable: + RayGen: + ShaderName: RayGen +DescriptorSets: + - Resources: + - Name: Output + Kind: RWStructuredBuffer + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 0 +DispatchParameters: + DispatchGroupCount: [ 2, 3, 1 ] +Results: + - Result: DispatchRaysDimensionsEcho + Rule: BufferExact + Actual: Output + Expected: Expected +... +#--- end + +# REQUIRES: raytracing-pipeline +# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268 +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o diff --git a/test/Feature/RT/dispatch-rays-index.test b/test/Feature/RT/dispatch-rays-index.test new file mode 100644 index 000000000..9e617751c --- /dev/null +++ b/test/Feature/RT/dispatch-rays-index.test @@ -0,0 +1,56 @@ +#--- source.hlsl + +[[vk::binding(0, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + // Each lane writes its own x-coordinate. With a 4x1x1 dispatch the four + // lanes must end up with Output[0..3] = {0, 1, 2, 3}. + Output[DispatchRaysIndex().x] = DispatchRaysIndex().x; +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen +Buffers: + - Name: Output + Format: UInt32 + Stride: 4 + FillSize: 16 + - Name: Expected + Format: UInt32 + Stride: 4 + Data: [ 0, 1, 2, 3 ] +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 0 +ShaderBindingTable: + RayGen: + ShaderName: RayGen +DescriptorSets: + - Resources: + - Name: Output + Kind: RWStructuredBuffer + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 0 +DispatchParameters: + DispatchGroupCount: [ 4, 1, 1 ] +Results: + - Result: DispatchRaysIndexX + Rule: BufferExact + Actual: Output + Expected: Expected +... +#--- end + +# REQUIRES: raytracing-pipeline +# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268 +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o diff --git a/test/Feature/RT/miss-shader-index.test b/test/Feature/RT/miss-shader-index.test new file mode 100644 index 000000000..a8908693e --- /dev/null +++ b/test/Feature/RT/miss-shader-index.test @@ -0,0 +1,127 @@ +#--- source.hlsl + +struct Payload { + uint Value; +}; + +[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0); +[[vk::binding(1, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + Payload P; + P.Value = 0; + RayDesc Ray; + // Origin far enough away from the triangle (which sits at z = 0) that any + // direction guarantees a miss — TMax keeps the ray short, so even a +z + // shoot doesn't accidentally find the BLAS. + Ray.Origin = float3(100, 100, 100); + Ray.Direction = float3(0, 0, 1); + Ray.TMin = 0.0; + Ray.TMax = 1.0; + // Each lane picks a different MissShaderIndex via the second-to-last + // TraceRay argument. + const uint MissIdx = DispatchRaysIndex().x; + TraceRay(Scene, RAY_FLAG_NONE, 0xFF, 0, 1, MissIdx, Ray, P); + Output[DispatchRaysIndex().x] = P.Value; +} + +[shader("miss")] +void Miss0(inout Payload P) { + P.Value = 0xAA; +} + +[shader("miss")] +void Miss1(inout Payload P) { + P.Value = 0xBB; +} + +[shader("closesthit")] +void ClosestHitMain(inout Payload P, in BuiltInTriangleIntersectionAttributes Attr) { + // Sentinel that should never be observed in this test — every ray misses. + P.Value = 0xDEAD; +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen + - Stage: Miss + Entry: Miss0 + - Stage: Miss + Entry: Miss1 + - Stage: ClosestHit + Entry: ClosestHitMain +Buffers: + - Name: Vertices + Format: Float32 + Stride: 12 + Data: [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ] + - Name: Output + Format: UInt32 + Stride: 4 + FillSize: 8 + - Name: Expected + Format: UInt32 + Stride: 4 + Data: [ 0xAA, 0xBB ] +AccelerationStructures: + BLAS: + - Name: TriangleBLAS + Triangles: + - VertexBuffer: Vertices + VertexFormat: RGB32Float + VertexStride: 12 + VertexCount: 3 + TLAS: + - Name: Scene + Instances: + - BLAS: TriangleBLAS +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 4 +HitGroups: + - Name: TriangleHitGroup + Type: Triangles + ClosestHit: ClosestHitMain +ShaderBindingTable: + RayGen: + ShaderName: RayGen + Miss: + - ShaderName: Miss0 + - ShaderName: Miss1 + HitGroup: + - ShaderName: TriangleHitGroup +DescriptorSets: + - Resources: + - Name: Scene + Kind: AccelerationStructure + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 0 + - Name: Output + Kind: RWStructuredBuffer + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 1 +DispatchParameters: + DispatchGroupCount: [ 2, 1, 1 ] +Results: + - Result: MissShaderIndexRouting + Rule: BufferExact + Actual: Output + Expected: Expected +... +#--- end + +# REQUIRES: raytracing-pipeline +# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268 +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o diff --git a/test/Feature/RT/ray-contribution-to-hit-group-index.test b/test/Feature/RT/ray-contribution-to-hit-group-index.test new file mode 100644 index 000000000..da65735a6 --- /dev/null +++ b/test/Feature/RT/ray-contribution-to-hit-group-index.test @@ -0,0 +1,130 @@ +#--- source.hlsl + +struct Payload { + uint Value; +}; + +[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0); +[[vk::binding(1, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + Payload P; + P.Value = 0; + RayDesc Ray; + Ray.Origin = float3(0, 0, 1); + Ray.Direction = float3(0, 0, -1); + Ray.TMin = 0.0; + Ray.TMax = 100.0; + // RayContributionToHitGroupIndex (4th TraceRay arg) selects which hit-group + // record the SBT routes to. Lane 0 picks hit-group 0 (writes 0xA1), lane 1 + // picks hit-group 1 (writes 0xB2). MultiplierForGeometryContributionToHit- + // GroupIndex (5th arg) stays 1; the BLAS has a single geometry so it + // doesn't add anything to the index. + const uint HGIdx = DispatchRaysIndex().x; + TraceRay(Scene, RAY_FLAG_NONE, 0xFF, HGIdx, 1, 0, Ray, P); + Output[DispatchRaysIndex().x] = P.Value; +} + +[shader("miss")] +void MissMain(inout Payload P) { + // Sentinel — never observed in this test since every ray hits the triangle. + P.Value = 0xDEAD; +} + +[shader("closesthit")] +void CHA(inout Payload P, in BuiltInTriangleIntersectionAttributes Attr) { + P.Value = 0xA1; +} + +[shader("closesthit")] +void CHB(inout Payload P, in BuiltInTriangleIntersectionAttributes Attr) { + P.Value = 0xB2; +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen + - Stage: Miss + Entry: MissMain + - Stage: ClosestHit + Entry: CHA + - Stage: ClosestHit + Entry: CHB +Buffers: + - Name: Vertices + Format: Float32 + Stride: 12 + Data: [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ] + - Name: Output + Format: UInt32 + Stride: 4 + FillSize: 8 + - Name: Expected + Format: UInt32 + Stride: 4 + Data: [ 0xA1, 0xB2 ] +AccelerationStructures: + BLAS: + - Name: TriangleBLAS + Triangles: + - VertexBuffer: Vertices + VertexFormat: RGB32Float + VertexStride: 12 + VertexCount: 3 + TLAS: + - Name: Scene + Instances: + - BLAS: TriangleBLAS +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 4 +HitGroups: + - Name: HitGroupA + Type: Triangles + ClosestHit: CHA + - Name: HitGroupB + Type: Triangles + ClosestHit: CHB +ShaderBindingTable: + RayGen: + ShaderName: RayGen + Miss: + - ShaderName: MissMain + HitGroup: + - ShaderName: HitGroupA + - ShaderName: HitGroupB +DescriptorSets: + - Resources: + - Name: Scene + Kind: AccelerationStructure + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 0 + - Name: Output + Kind: RWStructuredBuffer + DirectXBinding: + Register: 0 + Space: 0 + VulkanBinding: + Binding: 1 +DispatchParameters: + DispatchGroupCount: [ 2, 1, 1 ] +Results: + - Result: RayContributionRouting + Rule: BufferExact + Actual: Output + Expected: Expected +... +#--- end + +# REQUIRES: raytracing-pipeline +# Unimplemented https://github.com/llvm/offload-test-suite/issues/1268 +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target_lib -T lib_6_5 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o