summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/user-guide/03-convenience-features.md49
-rw-r--r--source/slang/hlsl.meta.slang110
-rw-r--r--source/slang/slang-ir-lower-dynamic-resource-heap.cpp16
-rw-r--r--tests/language-feature/descriptor-handle/desc-handle-2.slang2
-rw-r--r--tests/language-feature/descriptor-handle/desc-handle-default.slang159
-rw-r--r--tests/language-feature/descriptor-handle/desc-handle-vk-mutable-descriptor.slang79
6 files changed, 405 insertions, 10 deletions
diff --git a/docs/user-guide/03-convenience-features.md b/docs/user-guide/03-convenience-features.md
index 1e9a79882..2c2eb6f42 100644
--- a/docs/user-guide/03-convenience-features.md
+++ b/docs/user-guide/03-convenience-features.md
@@ -604,6 +604,24 @@ When targeting SPIRV, Slang will introduce a global array of descriptors and fet
The descriptor set ID of the global descriptor array can be configured with the `-bindless-space-index`
(or `CompilerOptionName::BindlessSpaceIndex` when using the API) option.
+Default behavior assigns the following binding-indicies to each descriptor type:
+```slang
+enum DefaultVkBindlessBindings : uint
+{
+ Sampler = 0, /// SAMPLER
+ CombinedTextureSampler = 1, /// COMBINED_IMAGE_SAMPLER
+ Texture_Read = 2, /// SAMPLED_IMAGE
+ Texture_ReadWrite = 3, /// STORAGE_IMAGE
+ TexelBuffer_Read = 4, /// UNIFORM_TEXEL_BUFFER
+ TexelBuffer_ReadWrite = 5, /// STORAGE_TEXEL_BUFFER
+ Buffer_Read = 6, /// UNIFORM_BUFFER
+ Buffer_ReadWrite = 7, /// STORAGE_BUFFER
+ Unknown = 8, /// Other
+}
+```
+
+`ACCELERATION_STRUCTURE` is excluded from the list of types since slang by default uses the provided handle to a `RaytracingAccelerationStructure` as a GPU address into the respective `RaytracingAccelerationStructure`, casting the 64bit handle into the type.
+
> #### Note
> The default implementation for SPIRV may change in the future if SPIRV is extended to provide what is
> equivalent to D3D's `ResourceDescriptorHeap` construct.
@@ -653,8 +671,32 @@ interface IOpaqueDescriptor
The user can call `defaultGetDescriptorFromHandle` function from their implementation of
`getDescriptorFromHandle` to dispatch to the default behavior.
-The `kind` and `descriptorAccess` constants allows user code to fetch from different locations
-depending on the type and access of the resource being requested. The `DescriptorKind` and
+Additionally, `defaultGetDescriptorFromHandle` has the parameter `constexpr BindlessDescriptorOptions bindlessOptions`. This parameter provides some alternative presets for how bindless indexes are assigned (currently only relevant to SPIRV):
+ ```slang
+public enum BindlessDescriptorOptions
+{
+ None = 0, /// Bind assuming regular binding model rules.
+ VkMutable = 1, /// Bind assuming `VK_EXT_mutable_descriptor_type` without mutable `AccelerationStructure` binding support.
+}
+ ```
+
+`VkMutable` provides the following bindings for descriptor types:
+```slang
+enum VkMutableBindlessBindings : uint
+{
+ Sampler = 0, /// SAMPLER
+ CombinedTextureSampler = 1, /// COMBINED_IMAGE_SAMPLER
+ Texture_Read = 2, /// SAMPLED_IMAGE
+ Texture_ReadWrite = 2, /// STORAGE_IMAGE
+ TexelBuffer_Read = 2, /// UNIFORM_TEXEL_BUFFER
+ TexelBuffer_ReadWrite = 2, /// STORAGE_TEXEL_BUFFER
+ Buffer_Read = 2, /// UNIFORM_BUFFER
+ Buffer_ReadWrite = 2, /// STORAGE_BUFFER,
+ Unknown = 3, /// Other
+}
+```
+
+The `kind` and `descriptorAccess` constants allows user code to fetch resources from different locations depending on the type and access of the resource being requested. The `DescriptorKind` and
`DescriptorAccess` enums are defined as:
```slang
@@ -666,6 +708,7 @@ enum DescriptorKind
Buffer, /// A buffer descriptor.
Sampler, /// A sampler state descriptor.
AccelerationStructure, /// A ray tracing acceleration structure descriptor.
+ TexelBuffer /// A texel buffer descriptor.
}
enum DescriptorAccess
@@ -692,6 +735,8 @@ void test(DescriptorHandle<Texture2D> t)
If the resource pointer value is not uniform and `nonuniform` is not called, the result may be
undefined.
+
+
Extensions
--------------------
Slang allows defining additional methods for a type outside its initial definition. For example, suppose we already have a type defined:
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 99f83776e..57cb188bb 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -22251,6 +22251,7 @@ enum DescriptorKind
Buffer, /// A buffer descriptor.
Sampler, /// A sampler state descriptor.
AccelerationStructure, /// A ray tracing acceleration structure descriptor.
+ TexelBuffer /// A texel buffer descriptor.
}
enum DescriptorAccess
@@ -22303,7 +22304,9 @@ extension _Texture<T, Shape, isArray, isMS, sampleCount, access, isShadow, isCom
typealias Handle = DescriptorHandle<This>;
- static const DescriptorKind kind = isCombined != 0 ? DescriptorKind.CombinedTextureSampler : DescriptorKind.Texture;
+ static const DescriptorKind kind = (Shape.flavor == __ShapeBuffer.flavor ?
+ DescriptorKind.TexelBuffer : (isCombined != 0 ? DescriptorKind.CombinedTextureSampler : DescriptorKind.Texture)
+);
static const DescriptorAccess descriptorAccess = (DescriptorAccess)access;
__implicit_conversion($(kConversionCost_ImplicitDereference))
@@ -22422,8 +22425,36 @@ __prefix T operator*(DescriptorHandle<T> value)
return getDescriptorFromHandle(value);
}
+// https://registry.khronos.org/vulkan/specs/latest/man/html/VkDescriptorType.html
+enum DefaultVkBindlessBindings : uint
+{
+ Sampler = 0, /// SAMPLER
+ CombinedTextureSampler = 1, /// COMBINED_IMAGE_SAMPLER
+ Texture_Read = 2, /// SAMPLED_IMAGE
+ Texture_ReadWrite = 3, /// STORAGE_IMAGE
+ TexelBuffer_Read = 4, /// UNIFORM_TEXEL_BUFFER
+ TexelBuffer_ReadWrite = 5, /// STORAGE_TEXEL_BUFFER
+ Buffer_Read = 6, /// UNIFORM_BUFFER
+ Buffer_ReadWrite = 7, /// STORAGE_BUFFER
+ Unknown = 8, /// Other
+}
+
+// https://github.com/KhronosGroup/Vulkan-Docs/blob/main/proposals/VK_EXT_mutable_descriptor_type.adoc
+enum VkMutableBindlessBindings : uint
+{
+ Sampler = 0, /// SAMPLER
+ CombinedTextureSampler = 1, /// COMBINED_IMAGE_SAMPLER
+ Texture_Read = 2, /// SAMPLED_IMAGE
+ Texture_ReadWrite = 2, /// STORAGE_IMAGE
+ TexelBuffer_Read = 2, /// UNIFORM_TEXEL_BUFFER
+ TexelBuffer_ReadWrite = 2, /// STORAGE_TEXEL_BUFFER
+ Buffer_Read = 2, /// UNIFORM_BUFFER
+ Buffer_ReadWrite = 2, /// STORAGE_BUFFER,
+ Unknown = 3, /// Other
+}
+
__intrinsic_op($(kIROp_GetDynamicResourceHeap))
-T[] __getDynamicResourceHeap<T:IOpaqueDescriptor>();
+T[] __getDynamicResourceHeap<T:IOpaqueDescriptor>(constexpr uint bindingIndex = 0);
// Used by the HLSL backend only, load a sampler state handle from SamplerDescriptorHeap.
@@ -22442,10 +22473,16 @@ T __makeCombinedTextureSamplerFromHandle<T, U>(U handle);
__intrinsic_op($(kIROp_CastDescriptorHandleToResource))
T __castDescriptorHandleToResource<T:IOpaqueDescriptor>(DescriptorHandle<T> ptr);
+public enum BindlessDescriptorOptions
+{
+ None = 0, /// Bind assuming regular binding model rules.
+ VkMutable = 1, /// Bind assuming `VK_EXT_mutable_descriptor_type` without mutable `AccelerationStructure` binding support.
+}
+
/// The default implementation of `getDescriptorFromHandle`, which converts from a descriptor handle
/// to a descriptor object.
[ForceInline]
-T defaultGetDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handleValue)
+T defaultGetDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handleValue, constexpr BindlessDescriptorOptions bindlessOptions = BindlessDescriptorOptions.None)
{
__target_switch
{
@@ -22458,6 +22495,73 @@ T defaultGetDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handle
return __loadResourceDescriptorFromHeap<T>(((uint2)handleValue).x);
case spirv:
case glsl:
+
+ switch(bindlessOptions)
+ {
+${{{{
+{
+ static const struct
+ {
+ char const* option;
+ char const* enumType;
+ }kBindlessOptions[] =
+ {
+ {"None", "DefaultVkBindlessBindings"},
+ {"VkMutable", "VkMutableBindlessBindings"},
+ };
+
+ for(auto bindlessOption : kBindlessOptions)
+ {
+ StringBuilder bindlessOptionIfElsePre;
+ StringBuilder bindlessOptionIfElsePost;
+
+ bindlessOptionIfElsePre << "case BindlessDescriptorOptions." << bindlessOption.option <<":\n{\n";
+ bindlessOptionIfElsePost << "}\n";
+}}}}
+ $(bindlessOptionIfElsePre.toString())
+ switch(T.kind)
+ {
+ case DescriptorKind.Sampler:
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).Sampler)[((uint2)handleValue).x];
+ case DescriptorKind.CombinedTextureSampler:
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).CombinedTextureSampler)[((uint2)handleValue).x];
+ case DescriptorKind.Texture:
+ {
+ if(DescriptorAccess.Read == T.descriptorAccess)
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).Texture_Read)[((uint2)handleValue).x];
+ else
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).Texture_ReadWrite)[((uint2)handleValue).x];
+ }
+ case DescriptorKind.TexelBuffer:
+ {
+ if(DescriptorAccess.Read == T.descriptorAccess)
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).TexelBuffer_Read)[((uint2)handleValue).x];
+ else
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).TexelBuffer_ReadWrite)[((uint2)handleValue).x];
+ }
+ case DescriptorKind.Buffer:
+ {
+ if(DescriptorAccess.Read == T.descriptorAccess)
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).Buffer_Read)[((uint2)handleValue).x];
+ else
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).Buffer_ReadWrite)[((uint2)handleValue).x];
+ }
+ case DescriptorKind.AccelerationStructure:
+ return __slang_noop_cast<T>(RaytracingAccelerationStructure(__asuint64((uint2)handleValue)));
+ default:
+ return __getDynamicResourceHeap<T>($(bindlessOption.enumType).Unknown)[((uint2)handleValue).x];
+ }
+ $(bindlessOptionIfElsePost.toString())
+${{{{
+ }
+}
+}}}}
+ default:
+ {
+ static_assert(false, "Impossible to end up here unless something went very wrong");
+ return __getDynamicResourceHeap<T>()[((uint2)handleValue).x];
+ }
+ }
case wgsl:
return __getDynamicResourceHeap<T>()[((uint2)handleValue).x];
default:
diff --git a/source/slang/slang-ir-lower-dynamic-resource-heap.cpp b/source/slang/slang-ir-lower-dynamic-resource-heap.cpp
index 60ec688a0..b2199a174 100644
--- a/source/slang/slang-ir-lower-dynamic-resource-heap.cpp
+++ b/source/slang/slang-ir-lower-dynamic-resource-heap.cpp
@@ -58,10 +58,11 @@ UInt findUnusedSpaceIndex(TargetProgram* targetProgram, IRModule* module)
return index;
}
-IRVarLayout* createResourceHeapVarLayoutWithSpace(
+IRVarLayout* createResourceHeapVarLayoutWithSpaceAndBinding(
IRBuilder& builder,
IRInst* param,
- UInt spaceIndex)
+ UInt spaceIndex,
+ UInt bindingIndex)
{
SLANG_UNUSED(param);
IRTypeLayout::Builder typeLayoutBuilder(&builder);
@@ -71,7 +72,8 @@ IRVarLayout* createResourceHeapVarLayoutWithSpace(
auto typeLayout = typeLayoutBuilder.build();
IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout);
varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::RegisterSpace)->offset = spaceIndex;
- varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::DescriptorTableSlot)->offset = 0;
+ varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::DescriptorTableSlot)->offset =
+ bindingIndex;
return varLayoutBuilder.build();
}
@@ -93,8 +95,14 @@ void lowerDynamicResourceHeap(TargetProgram* targetProgram, IRModule* module, Di
IRBuilder builder(inst);
builder.setInsertBefore(inst);
+ auto bindingIndex = (UInt)as<IRIntLit>(inst->getOperand(0))->getValue();
+
auto param = builder.createGlobalParam(arrayType);
- auto varLayout = createResourceHeapVarLayoutWithSpace(builder, param, unusedSpaceIndex);
+ auto varLayout = createResourceHeapVarLayoutWithSpaceAndBinding(
+ builder,
+ param,
+ unusedSpaceIndex,
+ bindingIndex);
builder.addLayoutDecoration(param, varLayout);
builder.addNameHintDecoration(param, toSlice("__slang_resource_heap"));
inst->replaceUsesWith(param);
diff --git a/tests/language-feature/descriptor-handle/desc-handle-2.slang b/tests/language-feature/descriptor-handle/desc-handle-2.slang
index fa7f73b31..f9eb02cbe 100644
--- a/tests/language-feature/descriptor-handle/desc-handle-2.slang
+++ b/tests/language-feature/descriptor-handle/desc-handle-2.slang
@@ -1,6 +1,6 @@
//TEST:SIMPLE(filecheck=SPV): -target spirv -bindless-space-index 101
-// SPV: OpDecorate %__slang_resource_heap{{.*}} Binding 0
+// SPV: OpDecorate %__slang_resource_heap{{.*}} Binding 1
// SPV: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 101
// SPV: OpImageSample
diff --git a/tests/language-feature/descriptor-handle/desc-handle-default.slang b/tests/language-feature/descriptor-handle/desc-handle-default.slang
new file mode 100644
index 000000000..d442fb610
--- /dev/null
+++ b/tests/language-feature/descriptor-handle/desc-handle-default.slang
@@ -0,0 +1,159 @@
+//TEST:SIMPLE(filecheck=SAMPLER): -target spirv -stage compute -entry computeMain -DSAMPLER
+//TEST:SIMPLE(filecheck=COMBINED_IMAGE_SAMPLER): -target spirv -stage compute -entry computeMain -DCOMBINED_IMAGE_SAMPLER
+//TEST:SIMPLE(filecheck=SAMPLED_IMAGE): -target spirv -stage compute -entry computeMain -DSAMPLED_IMAGE
+//TEST:SIMPLE(filecheck=STORAGE_IMAGE): -target spirv -stage compute -entry computeMain -DSTORAGE_IMAGE
+//TEST:SIMPLE(filecheck=UNIFORM_TEXEL_BUFFER): -target spirv -stage compute -entry computeMain -DUNIFORM_TEXEL_BUFFER
+//TEST:SIMPLE(filecheck=STORAGE_TEXEL_BUFFER): -target spirv -stage compute -entry computeMain -DSTORAGE_TEXEL_BUFFER
+//TEST:SIMPLE(filecheck=UNIFORM_BUFFER): -target spirv -stage compute -entry computeMain -DUNIFORM_BUFFER
+//TEST:SIMPLE(filecheck=STORAGE_BUFFER): -target spirv -stage compute -entry computeMain -DSTORAGE_BUFFER
+//TEST:SIMPLE(filecheck=ACCELERATION_STRUCTURE): -target spirv -stage compute -entry computeMain -DACCELERATION_STRUCTURE
+//TEST:SIMPLE(filecheck=SAMPLED_IMAGE): -target spirv -stage compute -entry computeMain -DSAMPLED_IMAGE
+//TEST:SIMPLE(filecheck=MIX): -target spirv -stage compute -entry computeMain -DSAMPLER -DSTORAGE_TEXEL_BUFFER -DUNIFORM_BUFFER -DACCELERATION_STRUCTURE
+
+// To intentionally fill up binding slots
+[[vk::binding(0, 1)]]
+RWTexture1D<float> t1;
+
+[[vk::binding(0, 2)]]
+RWTexture1D<float> t2;
+
+[[vk::binding(0, 4)]]
+RWTexture1D<float> t3;
+
+[[vk::binding(1, 4)]]
+Texture1D<float> t4;
+
+//MIX-DAG: OpDecorate %__slang_resource_heap{{.*}} Binding 0
+//MIX-DAG: OpDecorate %__slang_resource_heap{{.*}} Binding 5
+//MIX-DAG: OpDecorate %__slang_resource_heap{{.*}} Binding 6
+
+//MIX-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 1
+//MIX-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 2
+//MIX-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 3
+//MIX-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 4
+//MIX-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 7
+//MIX-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 8
+
+#ifdef SAMPLER
+//SAMPLER: OpDecorate %__slang_resource_heap{{.*}} Binding 0
+//SAMPLER-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 1
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 2
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 3
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 4
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 5
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 6
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 7
+//SAMPLER-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 8
+uniform SamplerState.Handle sampler;
+#endif
+
+#ifdef COMBINED_IMAGE_SAMPLER
+//COMBINED_IMAGE_SAMPLER: OpDecorate %__slang_resource_heap{{.*}} Binding 1
+//COMBINED_IMAGE_SAMPLER-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+uniform Sampler1DShadow.Handle combinedSampler;
+#endif
+
+#ifdef SAMPLED_IMAGE
+//SAMPLED_IMAGE: OpDecorate %__slang_resource_heap{{.*}} Binding 2
+//SAMPLED_IMAGE-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+uniform Texture1D<float>.Handle texture;
+#endif
+
+#ifdef STORAGE_IMAGE
+//STORAGE_IMAGE: OpDecorate %__slang_resource_heap{{.*}} Binding 3
+//STORAGE_IMAGE-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+uniform RWTexture1D<float>.Handle rwTexture1;
+uniform RWTexture2D<float>.Handle rwTexture2;
+#endif
+
+#ifdef UNIFORM_TEXEL_BUFFER
+//UNIFORM_TEXEL_BUFFER: OpDecorate %__slang_resource_heap{{.*}} Binding 4
+//UNIFORM_TEXEL_BUFFER-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+uniform Buffer<float>.Handle texelBuffer;
+#endif
+
+#ifdef STORAGE_TEXEL_BUFFER
+//STORAGE_TEXEL_BUFFER: OpDecorate %__slang_resource_heap{{.*}} Binding 5
+//STORAGE_TEXEL_BUFFER-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+uniform RWBuffer<float>.Handle rwTexelBuffer;
+#endif
+
+#ifdef UNIFORM_BUFFER
+//UNIFORM_BUFFER: OpDecorate %__slang_resource_heap{{.*}} Binding 6
+//UNIFORM_BUFFER-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+struct Data
+{
+ float v;
+}
+uniform ConstantBuffer<Data>.Handle buffer1;
+uniform StructuredBuffer<float>.Handle buffer2;
+#endif
+
+#ifdef STORAGE_BUFFER
+//STORAGE_BUFFER: OpDecorate %__slang_resource_heap{{.*}} Binding 7
+//STORAGE_BUFFER-NEXT: OpDecorate %__slang_resource_heap{{.*}} DescriptorSet 3
+uniform RWStructuredBuffer<float>.Handle rwBuffer;
+#endif
+
+#ifdef ACCELERATION_STRUCTURE
+//ACCELERATION_STRUCTURE-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 8
+//ACCELERATION_STRUCTURE: OpConvertUToAccelerationStructureKHR
+uniform RaytracingAccelerationStructure.Handle rayAcceleration;
+#endif
+
+[shader("compute")]
+void computeMain()
+{
+
+ t1[0] = t2[0] + t2[0] + t4[0];
+
+#ifdef SAMPLER
+ t1[2] = t4.Sample(sampler, 0);
+#endif
+
+#ifdef COMBINED_IMAGE_SAMPLER
+ t1[8] = combinedSampler.Sample(0);
+#endif
+
+#ifdef SAMPLED_IMAGE
+ t1[0] = texture[0];
+#endif
+
+#ifdef STORAGE_IMAGE
+ t1[11] = rwTexture1[0];
+ t1[12] = rwTexture2[0];
+#endif
+
+#ifdef UNIFORM_TEXEL_BUFFER
+ t1[10] = texelBuffer[0];
+#endif
+
+#ifdef STORAGE_TEXEL_BUFFER
+ t1[9] = rwTexelBuffer[0];
+#endif
+
+#ifdef UNIFORM_BUFFER
+ t1[4] = (*buffer1).v;
+ t1[6] = buffer2[0];
+#endif
+
+#ifdef STORAGE_BUFFER
+ t1[0] += rwBuffer[0];
+#endif
+
+#ifdef ACCELERATION_STRUCTURE
+ RayDesc ray;
+ ray.Origin = float3(0.1f, 0.1f, 0.0f);
+ ray.Direction = float3(0.0f, 0.0f, 1.0f);
+ ray.TMin = 0.0f;
+ ray.TMax = 100.0f;
+ RayQuery<RAY_FLAG_FORCE_NON_OPAQUE> rq;
+ rq.TraceRayInline(rayAcceleration, RAY_FLAG_FORCE_NON_OPAQUE, 0xff, ray);
+ bool proceed = rq.Proceed();
+ rq.CommitNonOpaqueTriangleHit();
+ rq.Abort();
+ t1[13] = (float)rq.RayFlags();
+#endif
+} \ No newline at end of file
diff --git a/tests/language-feature/descriptor-handle/desc-handle-vk-mutable-descriptor.slang b/tests/language-feature/descriptor-handle/desc-handle-vk-mutable-descriptor.slang
new file mode 100644
index 000000000..8fc2d3283
--- /dev/null
+++ b/tests/language-feature/descriptor-handle/desc-handle-vk-mutable-descriptor.slang
@@ -0,0 +1,79 @@
+//TEST:SIMPLE(filecheck=CHECK): -target spirv -stage compute -entry computeMain
+
+// To intentionally fill up binding slots
+[[vk::binding(0, 1)]]
+RWTexture1D<float> t1;
+
+[[vk::binding(0, 2)]]
+RWTexture1D<float> t2;
+
+[[vk::binding(0, 4)]]
+RWTexture1D<float> t3;
+
+[[vk::binding(1, 4)]]
+Texture1D<float> t4;
+
+//CHECK-DAG: OpDecorate %__slang_resource_heap{{.*}} Binding 0
+//CHECK-DAG: OpDecorate %__slang_resource_heap{{.*}} Binding 1
+//CHECK-DAG: OpDecorate %__slang_resource_heap{{.*}} Binding 2
+//CHECK-DAG: OpConvertUToAccelerationStructureKHR
+
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 3
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 4
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 5
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 6
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 7
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 8
+//CHECK-NOT: OpDecorate %__slang_resource_heap{{.*}} Binding 9
+export T getDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handleValue)
+{
+ return defaultGetDescriptorFromHandle(handleValue, BindlessDescriptorOptions.VkMutable);
+}
+
+uniform SamplerState.Handle sampler;
+uniform Sampler1DShadow.Handle combinedSampler;
+uniform Texture1D<float>.Handle texture;
+uniform RWTexture1D<float>.Handle rwTexture1;
+uniform RWTexture2D<float>.Handle rwTexture2;
+uniform Buffer<float>.Handle texelBuffer;
+uniform RWBuffer<float>.Handle rwTexelBuffer;
+
+struct Data
+{
+ float v;
+}
+uniform ConstantBuffer<Data>.Handle buffer1;
+uniform StructuredBuffer<float>.Handle buffer2;
+uniform RWStructuredBuffer<float>.Handle rwBuffer;
+uniform RaytracingAccelerationStructure.Handle rayAcceleration;
+
+
+[shader("compute")]
+void computeMain()
+{
+
+ t1[0] = t2[0] + t2[0] + t4[0];
+ t1[2] = t4.Sample(sampler, 0);
+ t1[8] = combinedSampler.Sample(0);
+ t1[0] = texture[0];
+ t1[11] = rwTexture1[0];
+ t1[12] = rwTexture2[0];
+ t1[10] = texelBuffer[0];
+ t1[9] = rwTexelBuffer[0];
+ t1[4] = (*buffer1).v;
+ t1[6] = buffer2[0];
+ t1[0] += rwBuffer[0];
+
+ RayDesc ray;
+ ray.Origin = float3(0.1f, 0.1f, 0.0f);
+ ray.Direction = float3(0.0f, 0.0f, 1.0f);
+ ray.TMin = 0.0f;
+ ray.TMax = 100.0f;
+ RayQuery<RAY_FLAG_FORCE_NON_OPAQUE> rq;
+ rq.TraceRayInline(rayAcceleration, RAY_FLAG_FORCE_NON_OPAQUE, 0xff, ray);
+ bool proceed = rq.Proceed();
+ rq.CommitNonOpaqueTriangleHit();
+ rq.Abort();
+ t1[13] = (float)rq.RayFlags();
+
+} \ No newline at end of file