From 046c7c3731d9a5884c80d790121118516d2bff11 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 3 Jun 2026 12:47:19 +0200 Subject: [PATCH] Cover ClosestHit ray system values: barycentrics, PrimitiveIndex, WorldRay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small PSO RT tests stacked on #1275, each isolating one shader- observable closest-hit system value from #1268's 👍 list. Same shape as the prior batch in #1277 — one .test file per behavior, single-purpose shader, exact buffer comparison. - `closest-hit-barycentrics.test` — 3-lane dispatch, each lane fires at a clearly-interior point of the single triangle so the closest- hit shader reports a known `BuiltInTriangleIntersectionAttributes ::barycentrics` (u, v). Points are picked from the inside of the triangle to avoid the watertight-traversal edge-rule lottery you hit at edge midpoints / vertices (the first cut of this test used midpoint(v0, v1) and one lane silently missed on both backends). - `closest-hit-primitive-index.test` — three triangles tiled at x = -3, 0, +3 in a single BLAS. 3-lane dispatch fires straight down at each triangle's centroid; the closest-hit reports `PrimitiveIndex()` and must match the lane index 0..2. - `closest-hit-world-ray.test` — 2-lane dispatch with rays from different z heights (1.0 and 2.0). Closest-hit packs `WorldRayOrigin().z`, `WorldRayDirection().z`, and `RayTCurrent()` through the payload; raygen flattens the float3 into a 6-element Float32 buffer. Verifies the system values match the raygen-side `RayDesc` and that t is correctly computed by the traversal. All three are `# REQUIRES: raytracing-pipeline` with `# XFAIL: Clang` — `clang-dxc` doesn't yet lower `[shader(…)]` entry points. With the Metal RT bring-up rebased on top, all three pass natively on Apple Silicon and Metal is dropped from the XFAIL list. Locally verified end-to-end on the user's Linux box: all three pass on Vulkan via the native offloader, and on D3D12 via Wine + vkd3d-proton + the cross-compiled `offloader.exe`, against an NVIDIA RTX 3060. And on macOS 15 / metal-irconverter 3.1.1 via the native offloader: all three PASS. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/Feature/RT/closest-hit-barycentrics.test | 133 ++++++++++++++++++ .../RT/closest-hit-primitive-index.test | 124 ++++++++++++++++ test/Feature/RT/closest-hit-world-ray.test | 128 +++++++++++++++++ 3 files changed, 385 insertions(+) create mode 100644 test/Feature/RT/closest-hit-barycentrics.test create mode 100644 test/Feature/RT/closest-hit-primitive-index.test create mode 100644 test/Feature/RT/closest-hit-world-ray.test diff --git a/test/Feature/RT/closest-hit-barycentrics.test b/test/Feature/RT/closest-hit-barycentrics.test new file mode 100644 index 000000000..ba6546188 --- /dev/null +++ b/test/Feature/RT/closest-hit-barycentrics.test @@ -0,0 +1,133 @@ +#--- source.hlsl + +struct Payload { + float2 Bary; +}; + +[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0); +[[vk::binding(1, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + // Each lane fires straight down at a clearly-interior point of the single + // triangle (vertices v0=(0,1,0), v1=(-1,-1,0), v2=(1,-1,0)). HLSL's + // closest-hit `barycentrics` attribute holds (u, v) with u + v + w == 1 + // and w being vertex-0's weight, so a hit point at + // P = w * v0 + u * v1 + v * v2 + // reports bary = (u, v). + // Lane 0 -> bary (0.25, 0.25), hit at 0.5*v0 + 0.25*v1 + 0.25*v2 = (0, 0, 0) + // Lane 1 -> bary (0.5, 0.25), hit at 0.25*v0 + 0.5*v1 + 0.25*v2 = (-0.25,-0.5, 0) + // Lane 2 -> bary (0.25, 0.5), hit at 0.25*v0 + 0.25*v1 + 0.5*v2 = (0.25, -0.5, 0) + // All three points sit well inside the triangle so the watertight- + // traversal edge rules are not in play and the hits are deterministic + // across rasterizers. Each lane writes 2 floats (u, v) at output offsets + // [2*Index .. 2*Index+1]. + const uint Idx = DispatchRaysIndex().x; + const float2 Targets[3] = {float2(0, 0), float2(-0.25, -0.5), + float2(0.25, -0.5)}; + + Payload P; + P.Bary = float2(0, 0); + RayDesc Ray; + Ray.Origin = float3(Targets[Idx], 1); + Ray.Direction = float3(0, 0, -1); + Ray.TMin = 0.0; + Ray.TMax = 100.0; + TraceRay(Scene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, Ray, P); + + Output[2 * Idx + 0] = P.Bary.x; + Output[2 * Idx + 1] = P.Bary.y; +} + +[shader("miss")] +void MissMain(inout Payload P) { + // Sentinel — every ray hits the triangle in this test. + P.Bary = float2(-1, -1); +} + +[shader("closesthit")] +void ClosestHitMain(inout Payload P, + in BuiltInTriangleIntersectionAttributes Attr) { + P.Bary = Attr.barycentrics; +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen + - Stage: Miss + Entry: MissMain + - 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: Float32 + Stride: 4 + FillSize: 24 + - Name: Expected + Format: Float32 + Stride: 4 + Data: [ 0.25, 0.25, 0.5, 0.25, 0.25, 0.5 ] +AccelerationStructures: + BLAS: + - Name: TriangleBLAS + Triangles: + - VertexBuffer: Vertices + VertexFormat: RGB32Float + VertexStride: 12 + VertexCount: 3 + TLAS: + - Name: Scene + Instances: + - BLAS: TriangleBLAS +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 8 +HitGroups: + - Name: TriangleHitGroup + Type: Triangles + ClosestHit: ClosestHitMain +ShaderBindingTable: + RayGen: + ShaderName: RayGen + Miss: + - ShaderName: MissMain + 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: [ 3, 1, 1 ] +Results: + - Result: ClosestHitBarycentrics + 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/closest-hit-primitive-index.test b/test/Feature/RT/closest-hit-primitive-index.test new file mode 100644 index 000000000..adb87923b --- /dev/null +++ b/test/Feature/RT/closest-hit-primitive-index.test @@ -0,0 +1,124 @@ +#--- source.hlsl + +struct Payload { + uint Prim; +}; + +[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0); +[[vk::binding(1, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + // Three triangles tiled along x at x = -3, 0, +3 in a single BLAS. Each + // lane shoots straight down at its own triangle's centroid; the closest- + // hit shader reports its `PrimitiveIndex()` and must match the lane index. + const float Centers[3] = {-3.0, 0.0, 3.0}; + const uint Idx = DispatchRaysIndex().x; + + Payload P; + P.Prim = 0xFFFFFFFF; + RayDesc Ray; + Ray.Origin = float3(Centers[Idx], 0, 1); + Ray.Direction = float3(0, 0, -1); + Ray.TMin = 0.0; + Ray.TMax = 100.0; + TraceRay(Scene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, Ray, P); + Output[Idx] = P.Prim; +} + +[shader("miss")] +void MissMain(inout Payload P) { + P.Prim = 0xDEAD; +} + +[shader("closesthit")] +void ClosestHitMain(inout Payload P, + in BuiltInTriangleIntersectionAttributes Attr) { + P.Prim = PrimitiveIndex(); +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen + - Stage: Miss + Entry: MissMain + - Stage: ClosestHit + Entry: ClosestHitMain +Buffers: + - Name: Vertices + Format: Float32 + Stride: 12 + # Three triangles in one BLAS, each ~1 unit wide, centroid x at -3 / 0 / +3. + Data: [ + -3.0, 1.0, 0.0, -4.0, -1.0, 0.0, -2.0, -1.0, 0.0, + 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, + 3.0, 1.0, 0.0, 2.0, -1.0, 0.0, 4.0, -1.0, 0.0, + ] + - Name: Output + Format: UInt32 + Stride: 4 + FillSize: 12 + - Name: Expected + Format: UInt32 + Stride: 4 + Data: [ 0, 1, 2 ] +AccelerationStructures: + BLAS: + - Name: ThreeTrianglesBLAS + Triangles: + - VertexBuffer: Vertices + VertexFormat: RGB32Float + VertexStride: 12 + VertexCount: 9 + TLAS: + - Name: Scene + Instances: + - BLAS: ThreeTrianglesBLAS +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 4 +HitGroups: + - Name: TriangleHitGroup + Type: Triangles + ClosestHit: ClosestHitMain +ShaderBindingTable: + RayGen: + ShaderName: RayGen + Miss: + - ShaderName: MissMain + 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: [ 3, 1, 1 ] +Results: + - Result: ClosestHitPrimitiveIndex + 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/closest-hit-world-ray.test b/test/Feature/RT/closest-hit-world-ray.test new file mode 100644 index 000000000..0a5594461 --- /dev/null +++ b/test/Feature/RT/closest-hit-world-ray.test @@ -0,0 +1,128 @@ +#--- source.hlsl + +struct Payload { + // Packed: (OriginZ, DirectionZ, RayTCurrent) per lane. + float3 Reading; +}; + +[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0); +[[vk::binding(1, 0)]] RWStructuredBuffer Output : register(u0); + +[shader("raygeneration")] +void RayGen() { + // Two lanes fire from different z heights at the single triangle (z=0). + // Lane 0: Origin.z = 1 -> ClosestHit sees Origin.z=1, Dir.z=-1, T=1. + // Lane 1: Origin.z = 2 -> ClosestHit sees Origin.z=2, Dir.z=-1, T=2. + // The ClosestHit reports WorldRayOrigin().z, WorldRayDirection().z, and + // RayTCurrent() through the payload. + const uint Idx = DispatchRaysIndex().x; + const float Origins[2] = {1.0, 2.0}; + + Payload P; + P.Reading = float3(0, 0, 0); + RayDesc Ray; + Ray.Origin = float3(0, 0, Origins[Idx]); + Ray.Direction = float3(0, 0, -1); + Ray.TMin = 0.0; + Ray.TMax = 100.0; + TraceRay(Scene, RAY_FLAG_NONE, 0xFF, 0, 1, 0, Ray, P); + + Output[3 * Idx + 0] = P.Reading.x; + Output[3 * Idx + 1] = P.Reading.y; + Output[3 * Idx + 2] = P.Reading.z; +} + +[shader("miss")] +void MissMain(inout Payload P) { + // Sentinel — every ray hits the triangle. + P.Reading = float3(-1, -1, -1); +} + +[shader("closesthit")] +void ClosestHitMain(inout Payload P, + in BuiltInTriangleIntersectionAttributes Attr) { + P.Reading.x = WorldRayOrigin().z; + P.Reading.y = WorldRayDirection().z; + P.Reading.z = RayTCurrent(); +} +//--- pipeline.yaml +--- +Shaders: + - Stage: RayGeneration + Entry: RayGen + - Stage: Miss + Entry: MissMain + - 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: Float32 + Stride: 4 + FillSize: 24 + - Name: Expected + Format: Float32 + Stride: 4 + Data: [ 1.0, -1.0, 1.0, 2.0, -1.0, 2.0 ] +AccelerationStructures: + BLAS: + - Name: TriangleBLAS + Triangles: + - VertexBuffer: Vertices + VertexFormat: RGB32Float + VertexStride: 12 + VertexCount: 3 + TLAS: + - Name: Scene + Instances: + - BLAS: TriangleBLAS +RayTracingPipelineConfig: + MaxTraceRecursionDepth: 1 + MaxPayloadSizeInBytes: 12 +HitGroups: + - Name: TriangleHitGroup + Type: Triangles + ClosestHit: ClosestHitMain +ShaderBindingTable: + RayGen: + ShaderName: RayGen + Miss: + - ShaderName: MissMain + 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: ClosestHitWorldRay + 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