Skip to content

Commit 726e097

Browse files
authored
Fix descriptor heap acceleration structure loads (#11209)
Fixes #10671 ## Summary Fixes #10671 — `DescriptorHandle<RaytracingAccelerationStructure>` with `spvDescriptorHeapEXT` produced invalid SPIR-V (missing `OpConvertUToAccelerationStructureKHR`), causing `VK_ERROR_DEVICE_LOST` at runtime. - **SPIR-V emitter**: add `emitAccelerationStructureFromDescriptorHeap` that emits `OpUntypedAccessChainKHR` + `OpLoad` + `OpConvertUToAccelerationStructureKHR` when loading a `RaytracingAccelerationStructure` from a descriptor heap. Route `kIROp_SPIRVLoadDescriptorFromHeap` instructions to this path when the (unwrapped) data type is `kIROp_RaytracingAccelerationStructureType`. - **Capability/extension**: use `requireSPIRVAnyCapability({RayTracingKHR, RayQueryKHR})` and `ensureAnyExtensionDeclaration({"SPV_KHR_ray_tracing", "SPV_KHR_ray_query"})` to match how the AS type itself is declared, supporting both ray-tracing and ray-query targets. - **render-test**: guard `assignAccelerationStructure` — when the cursor expects a descriptor handle type, return `SLANG_E_NOT_AVAILABLE` if the acceleration structure is null (device doesn't support ray tracing), and propagate `getDescriptorHandle` failure rather than silently falling back to `setBinding`. - **Regression test**: `tests/spirv/descriptor-heap-acceleration-structure.slang` verifies the correct SPIR-V sequence is emitted. ## Test plan - [ ] `slang-test tests/spirv/descriptor-heap-acceleration-structure.slang` passes - [ ] CI passes on SPIR-V targets ## Labels pr: non-breaking
1 parent a49a125 commit 726e097

5 files changed

Lines changed: 185 additions & 10 deletions

File tree

docs/user-guide/03-convenience-features.md

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -661,10 +661,12 @@ void test()
661661

662662
When targeting SPIR-V, Slang can generate two flavors of code for descriptor handles depending on whether
663663
the `spvDescriptorHeapEXT` capability is declared or requested on the compilation request.
664-
By default (without requesting `spvDescriptorHeapEXT`), Slang introduces a global array of descriptors and
665-
fetches from the global array.
666-
The descriptor set ID of the global descriptor array can be configured with the `-bindless-space-index`
667-
(or `CompilerOptionName::BindlessSpaceIndex` when using the API) option.
664+
665+
### Default Slang Behavior
666+
667+
Without requesting `spvDescriptorHeapEXT`, Slang introduces a global array of descriptors and fetches
668+
from it. The descriptor set ID of the global descriptor array can be configured with the
669+
`-bindless-space-index` (or `CompilerOptionName::BindlessSpaceIndex` when using the API) option.
668670

669671
Default behavior assigns binding indices based on descriptor types:
670672

@@ -682,13 +684,32 @@ Default behavior assigns binding indices based on descriptor types:
682684

683685
> `ACCELERATION_STRUCTURE` is excluded from the list of types since Slang by default uses the handle to a `RaytracingAccelerationStructure` as a GPU address, casting the handle to a `RaytracingAccelerationStructure`. This removes the need for a binding-slot of `RaytracingAccelerationStructure`.
684686
685-
When the `spvDescriptorHeapEXT` capability is requested (either via the `-capability` command-line option or via the compilation API), Slang will map descriptor handles to the `SPV_EXT_descriptor_heap` extension
686-
without declaring any explicit descriptor sets. Descriptor handles are still lowered to `uint2`.
687-
For resources other than `CombinedTextureSampler`, Slang uses `uint2.x` as the index to access the global sampler or resource heap.
688-
For `CombinedTextureSampler` handles, Slang uses `uint2.x` to index into the resource heap to obtain the texture, and `uint2.y` to index into the sampler
689-
heap to obtain the sampler state, and combines the objects with an `OpSampledImage` instruction. By default, when using the `spvDescriptorHeapEXT` capability, Slang will reinterpret the resource or sampler heap as an array of requested resource type, whose stride is defined by the resource type, obtained from `OpConstantSizeOfEXT` instruction. The user can override this behavior and specify a different stride with the `-spirv-resource-heap-stride` or `-spirv-sampler-heap-stride` compiler options.
687+
### SPIR-V with `spvDescriptorHeapEXT`
690688

691-
Additionally, if none of the above behaviors are desired, users can customize the conversion logic from descriptor handle to resource objects by providing a `getDescriptorFromHandle` in user code. For example:
689+
When the `spvDescriptorHeapEXT` capability is requested (either via the `-capability` command-line option
690+
or via the compilation API), Slang maps descriptor handles to the `SPV_EXT_descriptor_heap` extension
691+
without declaring any explicit descriptor sets. Descriptor handles are still lowered to `uint2`.
692+
For resources other than `CombinedTextureSampler`, Slang uses `uint2.x` as the index to access the global
693+
sampler or resource heap. For `CombinedTextureSampler` handles, Slang uses `uint2.x` to index into the
694+
resource heap to obtain the texture, and `uint2.y` to index into the sampler heap to obtain the sampler
695+
state, and combines the objects with an `OpSampledImage` instruction.
696+
697+
By default, when using the `spvDescriptorHeapEXT` capability, Slang reinterprets the resource or sampler
698+
heap as an array of the requested resource type, whose stride is defined by the resource type and obtained
699+
from the `OpConstantSizeOfEXT` instruction. The user can override this behavior and specify a different
700+
stride with the `-spirv-resource-heap-stride` or `-spirv-sampler-heap-stride` compiler options.
701+
702+
> **Note on `RaytracingAccelerationStructure`:** When the `spvDescriptorHeapEXT` capability is active and
703+
> a `DescriptorHandle<RaytracingAccelerationStructure>` is dereferenced, Slang loads a 64-bit device address
704+
> from the descriptor heap and converts it to an acceleration structure handle via
705+
> `OpConvertUToAccelerationStructureKHR`. This matches how GPU drivers expose acceleration structure
706+
> descriptors in the heap (as device addresses), and requires either the `SPV_KHR_ray_tracing` or
707+
> `SPV_KHR_ray_query` extension.
708+
709+
### Custom Descriptor Fetch
710+
711+
If none of the above behaviors are desired, users can customize the conversion logic from descriptor handle
712+
to resource objects by providing a `getDescriptorFromHandle` in user code. For example:
692713

693714
```slang
694715
// All texture and buffer handles are defined in descriptor set 100.

source/slang/slang-emit-spirv.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4917,6 +4917,16 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
49174917
case kIROp_SPIRVLoadDescriptorFromHeap:
49184918
{
49194919
auto loadDesc = as<IRSPIRVLoadDescriptorFromHeap>(inst);
4920+
if (unwrapAttributedType(inst->getDataType())->getOp() ==
4921+
kIROp_RaytracingAccelerationStructureType)
4922+
{
4923+
result = emitAccelerationStructureFromDescriptorHeap(
4924+
parent,
4925+
inst,
4926+
loadDesc->getHeap(),
4927+
loadDesc->getIndex());
4928+
break;
4929+
}
49204930
result =
49214931
emitDescriptorHeapLoad(parent, inst, loadDesc->getHeap(), loadDesc->getIndex());
49224932
break;
@@ -7114,6 +7124,38 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
71147124
return getDescriptorRuntimeArrayType(descriptorElementType);
71157125
}
71167126

7127+
SpvInst* emitAccelerationStructureFromDescriptorHeap(
7128+
SpvInstParent* parent,
7129+
IRInst* inst,
7130+
IRInst* heap,
7131+
IRInst* index)
7132+
{
7133+
requireSPIRVAnyCapability({SpvCapabilityRayTracingKHR, SpvCapabilityRayQueryKHR});
7134+
ensureAnyExtensionDeclaration(
7135+
{UnownedStringSlice("SPV_KHR_ray_tracing"), UnownedStringSlice("SPV_KHR_ray_query")});
7136+
7137+
IRBuilder builder(m_irModule);
7138+
builder.setInsertInto(m_irModule->getModuleInst());
7139+
auto addressType = builder.getUInt64Type();
7140+
auto valuePtr = emitInst(
7141+
parent,
7142+
nullptr,
7143+
SpvOpUntypedAccessChainKHR,
7144+
ensureUntypedPointerType(SpvStorageClassUniformConstant),
7145+
kResultID,
7146+
getDescriptorHeapBaseType(as<IRType>(unwrapAttributedType(inst->getDataType()))),
7147+
heap,
7148+
index);
7149+
auto address = emitOpLoad(parent, nullptr, addressType, valuePtr);
7150+
return emitInst(
7151+
parent,
7152+
inst,
7153+
SpvOpConvertUToAccelerationStructureKHR,
7154+
inst->getDataType(),
7155+
kResultID,
7156+
address);
7157+
}
7158+
71177159
SpvInst* emitDescriptorHeapLoad(
71187160
SpvInstParent* parent,
71197161
IRInst* inst,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Companion to descriptor-heap-acceleration-structure.slang.
2+
// Exercises the RayTracingKHR branch of requireSPIRVAnyCapability when
3+
// loading a RaytracingAccelerationStructure from a descriptor heap in a
4+
// raygeneration shader (which already requires SpvCapabilityRayTracingKHR).
5+
//TEST:SIMPLE(filecheck=CHECK): -target spirv-asm -stage raygeneration -entry rayGenMain -profile sm_6_6 -capability spvDescriptorHeapEXT -spirv-resource-heap-stride 32
6+
7+
struct PushConstants
8+
{
9+
DescriptorHandle<RaytracingAccelerationStructure> accelerationStructure;
10+
DescriptorHandle<RWTexture2D<float4>> outputTexture;
11+
}
12+
13+
struct RayPayload
14+
{
15+
float t;
16+
}
17+
18+
[shader("raygeneration")]
19+
void rayGenMain(uniform PushConstants pushConstants)
20+
{
21+
// CHECK-DAG: %[[U64:[A-Za-z0-9_]+]] = OpTypeInt 64 0
22+
// CHECK-DAG: %[[AS_TYPE:[A-Za-z0-9_]+]] = OpTypeAccelerationStructureKHR
23+
// CHECK-DAG: %[[AS_HEAP_ARRAY:[A-Za-z0-9_]+]] = OpTypeRuntimeArray %[[AS_TYPE]]
24+
// CHECK-DAG: OpDecorate %[[AS_HEAP_ARRAY]] ArrayStride 32
25+
// CHECK-DAG: OpCapability RayTracingKHR
26+
// CHECK: OpUntypedAccessChainKHR %_ptr_UniformConstant %[[AS_HEAP_ARRAY]] %slang_resourceHeap
27+
// CHECK-NEXT: %[[AS_ADDR:[A-Za-z0-9_]+]] = OpLoad %[[U64]]
28+
// CHECK-NEXT: OpConvertUToAccelerationStructureKHR %[[AS_TYPE]] %[[AS_ADDR]]
29+
// CHECK: OpTraceRayKHR
30+
RaytracingAccelerationStructure scene =
31+
RaytracingAccelerationStructure(pushConstants.accelerationStructure);
32+
33+
RayPayload payload = { 0.0 };
34+
RayDesc ray;
35+
ray.Origin = float3(0.1, 0.1, 0.0);
36+
ray.TMin = 0.01f;
37+
ray.Direction = float3(0.0, 0.0, 1.0);
38+
ray.TMax = 1e4f;
39+
TraceRay(scene, RAY_FLAG_NONE, 0xff, 0, 1, 0, ray, payload);
40+
41+
RWTexture2D<float4> outputTexture = pushConstants.outputTexture;
42+
outputTexture[uint2(0, 0)] = float4(payload.t, 0, 0, 1);
43+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Regression test for GitHub issue #10671.
2+
//TEST:SIMPLE(filecheck=CHECK): -target spirv-asm -stage compute -entry computeMain -profile sm_6_6 -capability spvDescriptorHeapEXT -capability spvRayQueryKHR -spirv-resource-heap-stride 32
3+
4+
struct PushConstants
5+
{
6+
DescriptorHandle<RaytracingAccelerationStructure> accelerationStructure;
7+
DescriptorHandle<RWTexture2D<float4>> outputTexture;
8+
}
9+
10+
bool traceRayClosestHit(
11+
RaytracingAccelerationStructure scene,
12+
float3 rayOrigin,
13+
float3 rayDirection,
14+
out float t)
15+
{
16+
RayDesc ray;
17+
ray.Origin = rayOrigin;
18+
ray.TMin = 0.01f;
19+
ray.Direction = rayDirection;
20+
ray.TMax = 1e4f;
21+
22+
RayQuery<RAY_FLAG_NONE> rayQuery;
23+
rayQuery.TraceRayInline(scene, RAY_FLAG_NONE, 0xff, ray);
24+
rayQuery.Proceed();
25+
26+
if (rayQuery.CommittedStatus() == COMMITTED_TRIANGLE_HIT)
27+
{
28+
t = rayQuery.CommittedRayT();
29+
return true;
30+
}
31+
32+
unused(t);
33+
return false;
34+
}
35+
36+
[shader("compute")]
37+
[numthreads(1, 1, 1)]
38+
void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID, uniform PushConstants pushConstants)
39+
{
40+
// CHECK-DAG: %[[U64:[A-Za-z0-9_]+]] = OpTypeInt 64 0
41+
// CHECK-DAG: %[[AS_TYPE:[A-Za-z0-9_]+]] = OpTypeAccelerationStructureKHR
42+
// CHECK-DAG: %[[AS_HEAP_ARRAY:[A-Za-z0-9_]+]] = OpTypeRuntimeArray %[[AS_TYPE]]
43+
// CHECK-DAG: OpDecorate %[[AS_HEAP_ARRAY]] ArrayStride 32
44+
// CHECK-DAG: OpCapability RayQueryKHR
45+
// CHECK: OpUntypedAccessChainKHR %_ptr_UniformConstant %[[AS_HEAP_ARRAY]] %slang_resourceHeap
46+
// CHECK-NEXT: %[[AS_ADDR:[A-Za-z0-9_]+]] = OpLoad %[[U64]]
47+
// CHECK-NEXT: OpConvertUToAccelerationStructureKHR %[[AS_TYPE]] %[[AS_ADDR]]
48+
// CHECK: OpRayQueryInitializeKHR
49+
float t = 0.0;
50+
traceRayClosestHit(
51+
RaytracingAccelerationStructure(pushConstants.accelerationStructure),
52+
float3(0.1, 0.1, 0.0),
53+
float3(0.0, 0.0, 1.0),
54+
t);
55+
56+
RWTexture2D<float4> outputTexture = pushConstants.outputTexture;
57+
outputTexture[dispatchThreadID.xy] = t;
58+
}

tools/render-test/render-test-main.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,17 @@ struct AssignValsFromLayoutContext
862862
ShaderCursor const& dstCursor,
863863
ShaderInputLayout::AccelerationStructureVal* srcVal)
864864
{
865+
SLANG_UNUSED(srcVal);
866+
if (isDescriptorHandleType(dstCursor))
867+
{
868+
if (!accelerationStructure)
869+
return SLANG_E_NOT_AVAILABLE;
870+
DescriptorHandle handle;
871+
SLANG_RETURN_ON_FAIL(accelerationStructure->getDescriptorHandle(&handle));
872+
SLANG_RETURN_ON_FAIL(dstCursor.setDescriptorHandle(handle));
873+
return SLANG_OK;
874+
}
875+
865876
dstCursor.setBinding(accelerationStructure);
866877
return SLANG_OK;
867878
}

0 commit comments

Comments
 (0)