summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/command-line-slangc-reference.md2
-rw-r--r--source/slang/hlsl.meta.slang54
-rw-r--r--source/slang/slang-capabilities.capdef7
-rw-r--r--source/slang/slang-emit-c-like.cpp2
-rw-r--r--source/slang/slang-emit-metal.cpp53
-rw-r--r--source/slang/slang-emit-metal.h1
-rw-r--r--source/slang/slang-emit-spirv.cpp87
-rw-r--r--source/slang/slang-ir-insts-stable-names.lua2
-rw-r--r--source/slang/slang-ir-insts.lua2
-rw-r--r--source/slang/slang-ir.cpp2
-rw-r--r--tests/language-feature/descriptor-handle/desc-handle-nv-bindless-texture.slang64
-rw-r--r--tests/metal/atomic-texture-buffer.slang60
12 files changed, 286 insertions, 50 deletions
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md
index 3e250df88..9c828462c 100644
--- a/docs/command-line-slangc-reference.md
+++ b/docs/command-line-slangc-reference.md
@@ -1207,6 +1207,7 @@ A capability describes an optional feature that a target may or may not support.
* `SPV_KHR_cooperative_matrix` : enables the SPV_KHR_cooperative_matrix extension
* `SPV_NV_tensor_addressing` : enables the SPV_NV_tensor_addressing extension
* `SPV_NV_cooperative_matrix2` : enables the SPV_NV_cooperative_matrix2 extension
+* `SPV_NV_bindless_texture` : enables the SPV_NV_bindless_texture extension
* `spvDeviceGroup`
* `spvAtomicFloat32AddEXT`
* `spvAtomicFloat16AddEXT`
@@ -1259,6 +1260,7 @@ A capability describes an optional feature that a target may or may not support.
* `spvQuadControlKHR`
* `spvVulkanMemoryModelKHR`
* `spvVulkanMemoryModelDeviceScopeKHR`
+* `spvBindlessTextureNV`
* `metallib_latest`
* `dxil_lib`
* `any_target`
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 35d210da6..bf5039905 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -17181,7 +17181,7 @@ ${{{{
// If a 'Texture[index]' is referred to by a '__ref', call 'kIROp_ImageSubscript(index)'.
// This allows call's to stay aware that the input is from a 'Texture'.
__intrinsic_op($(kIROp_ImageSubscript))
- [nonmutating]
+ [constref]
ref;
${{{{
} // access != SLANG_RESOURCE_ACCESS_READ
@@ -22921,14 +22921,51 @@ struct DescriptorHandle<T:IOpaqueDescriptor> : IComparable
__intrinsic_op($(kIROp_CastUInt2ToDescriptorHandle))
__init(uint2 handleValue);
+ /// Constructor for uint64_t handles
[ForceInline]
- bool equals(DescriptorHandle<T> other) { return all(__vectorEql((uint2)this, (uint2)other)); }
+ [require(spvBindlessTextureNV)]
+ __intrinsic_op($(kIROp_CastUInt64ToDescriptorHandle))
+ __init(uint64_t handleValue);
[ForceInline]
- bool lessThan(DescriptorHandle<T> other) { let vthis = ((uint2)this); let vother = (uint2)other; return vthis.x < vother.x || (vthis.x == vother.x && vthis.y < vother.y); }
+ bool equals(DescriptorHandle<T> other)
+ {
+ __target_switch
+ {
+ case spvBindlessTextureNV:
+ return (uint64_t)this == (uint64_t)other;
+ default:
+ return all(__vectorEql((uint2)this, (uint2)other));
+ }
+ }
[ForceInline]
- bool lessThanOrEquals(DescriptorHandle<T> other) { let vthis = ((uint2)this); let vother = (uint2)other; return vthis.x < vother.x || (vthis.x == vother.x && vthis.y <= vother.y); }
+ bool lessThan(DescriptorHandle<T> other)
+ {
+ __target_switch
+ {
+ case spvBindlessTextureNV:
+ return (uint64_t)this < (uint64_t)other;
+ default:
+ let vthis = ((uint2)this);
+ let vother = (uint2)other;
+ return vthis.x < vother.x || (vthis.x == vother.x && vthis.y < vother.y);
+ }
+ }
+
+ [ForceInline]
+ bool lessThanOrEquals(DescriptorHandle<T> other)
+ {
+ __target_switch
+ {
+ case spvBindlessTextureNV:
+ return (uint64_t)this <= (uint64_t)other;
+ default:
+ let vthis = ((uint2)this);
+ let vother = (uint2)other;
+ return vthis.x < vother.x || (vthis.x == vother.x && vthis.y <= vother.y);
+ }
+ }
}
extension uint2
@@ -22940,6 +22977,13 @@ extension uint2
__init<T:IOpaqueDescriptor>(DescriptorHandle<T> bindless);
}
+extension uint64_t
+{
+ __intrinsic_op($(kIROp_CastDescriptorHandleToUInt64))
+ [require(spvBindlessTextureNV)]
+ __init<T:IOpaqueDescriptor>(DescriptorHandle<T> bindless);
+}
+
__generic<T:IOpaqueDescriptor>
[ForceInline]
__prefix T operator*(DescriptorHandle<T> value)
@@ -23015,6 +23059,8 @@ T defaultGetDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handle
return __makeCombinedTextureSamplerFromHandle<T>((uint2)handleValue);
else
return __loadResourceDescriptorFromHeap<T>(((uint2)handleValue).x);
+ case spvBindlessTextureNV:
+ return __castDescriptorHandleToResource<T>(handleValue);
case spirv:
case glsl:
diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef
index ff9697f7d..a747e701c 100644
--- a/source/slang/slang-capabilities.capdef
+++ b/source/slang/slang-capabilities.capdef
@@ -601,6 +601,9 @@ def SPV_NV_tensor_addressing : _spirv_1_6;
/// [EXT]
def SPV_NV_cooperative_matrix2 : SPV_NV_tensor_addressing + SPV_KHR_cooperative_matrix;
+/// Represents the SPIR-V extension for SPV_NV_bindless_texture.
+/// [EXT]
+def SPV_NV_bindless_texture: _spirv_1_0;
// SPIRV Capabilities.
@@ -812,6 +815,10 @@ def spvVulkanMemoryModelKHR : SPV_KHR_vulkan_memory_model;
/// [EXT]
def spvVulkanMemoryModelDeviceScopeKHR : SPV_KHR_vulkan_memory_model;
+/// Represents the SPIR-V capability for the bindless texture.
+/// [EXT]
+def spvBindlessTextureNV : SPV_NV_bindless_texture;
+
// The following capabilities all pertain to how ray tracing shaders are translated
// to GLSL, where there are two different extensions that can provide the core
// functionality of `TraceRay` and the related operations.
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index af4345942..08caedb94 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -2543,6 +2543,8 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
case kIROp_CastDescriptorHandleToUInt2:
case kIROp_CastUInt2ToDescriptorHandle:
case kIROp_CastDescriptorHandleToResource:
+ case kIROp_CastUInt64ToDescriptorHandle:
+ case kIROp_CastDescriptorHandleToUInt64:
emitOperand(inst->getOperand(0), outerPrec);
break;
// Binary ops
diff --git a/source/slang/slang-emit-metal.cpp b/source/slang/slang-emit-metal.cpp
index b19e85044..71de65ee7 100644
--- a/source/slang/slang-emit-metal.cpp
+++ b/source/slang/slang-emit-metal.cpp
@@ -290,9 +290,35 @@ static IRImageSubscript* isTextureAccess(IRInst* inst)
return as<IRImageSubscript>(getRootAddr(inst->getOperand(0)));
}
+void MetalSourceEmitter::emitImageOperandWithAccessor(IRInst* imageOperand)
+{
+ emitOperand(imageOperand, getInfo(EmitOp::Postfix));
+
+ // Check if the image operand is a pointer type
+ if (as<IRPtrTypeBase>(imageOperand->getDataType()))
+ {
+ m_writer->emit("->");
+ }
+ else
+ {
+ m_writer->emit(".");
+ }
+}
+
void MetalSourceEmitter::emitAtomicImageCoord(IRImageSubscript* inst)
{
- auto resourceType = as<IRResourceTypeBase>(inst->getImage()->getDataType());
+ auto imageDataType = inst->getImage()->getDataType();
+ auto resourceType = as<IRResourceTypeBase>(imageDataType);
+
+ // If the image data type is a pointer, get the value type
+ if (!resourceType)
+ {
+ if (auto ptrType = as<IRPtrTypeBase>(imageDataType))
+ {
+ resourceType = as<IRResourceTypeBase>(ptrType->getValueType());
+ }
+ }
+
if (auto textureType = as<IRTextureType>(resourceType))
{
if (as<IRVectorType>(textureType->getElementType()))
@@ -303,7 +329,7 @@ void MetalSourceEmitter::emitAtomicImageCoord(IRImageSubscript* inst)
"atomic operation on non-scalar texture");
}
}
- bool isArray = getIntVal(resourceType->getIsArrayInst()) != 0;
+ bool isArray = resourceType && getIntVal(resourceType->getIsArrayInst()) != 0;
if (isArray)
{
emitOperand(inst->getCoord(), getInfo(EmitOp::Postfix));
@@ -375,8 +401,7 @@ bool MetalSourceEmitter::tryEmitInstStmtImpl(IRInst* inst)
bool isImageOp = false;
if (auto imageSubscript = isTextureAccess(inst))
{
- emitOperand(imageSubscript->getImage(), getInfo(EmitOp::Postfix));
- m_writer->emit(".");
+ emitImageOperandWithAccessor(imageSubscript->getImage());
m_writer->emit(imageFunc);
m_writer->emit("(");
emitAtomicImageCoord(imageSubscript);
@@ -439,8 +464,8 @@ bool MetalSourceEmitter::tryEmitInstStmtImpl(IRInst* inst)
bool isImageOp = false;
if (auto imageSubscript = isTextureAccess(inst))
{
- emitOperand(imageSubscript->getImage(), getInfo(EmitOp::Postfix));
- m_writer->emit(".atomic_load(");
+ emitImageOperandWithAccessor(imageSubscript->getImage());
+ m_writer->emit("atomic_load(");
emitAtomicImageCoord(imageSubscript);
isImageOp = true;
}
@@ -465,8 +490,8 @@ bool MetalSourceEmitter::tryEmitInstStmtImpl(IRInst* inst)
bool isImageOp = false;
if (auto imageSubscript = isTextureAccess(inst))
{
- emitOperand(imageSubscript->getImage(), getInfo(EmitOp::Postfix));
- m_writer->emit(".atomic_store(");
+ emitImageOperandWithAccessor(imageSubscript->getImage());
+ m_writer->emit("atomic_store(");
emitAtomicImageCoord(imageSubscript);
isImageOp = true;
}
@@ -515,8 +540,8 @@ bool MetalSourceEmitter::tryEmitInstStmtImpl(IRInst* inst)
m_writer->emit(";\n");
if (imageSubscript)
{
- emitOperand(imageSubscript->getImage(), getInfo(EmitOp::Postfix));
- m_writer->emit(".atomic_compare_exchange_weak(");
+ emitImageOperandWithAccessor(imageSubscript->getImage());
+ m_writer->emit("atomic_compare_exchange_weak(");
emitAtomicImageCoord(imageSubscript);
}
else
@@ -588,8 +613,8 @@ bool MetalSourceEmitter::tryEmitInstStmtImpl(IRInst* inst)
bool isImageOp = false;
if (auto imageSubscript = isTextureAccess(inst))
{
- emitOperand(imageSubscript->getImage(), getInfo(EmitOp::Postfix));
- m_writer->emit(".atomic_fetch_add(");
+ emitImageOperandWithAccessor(imageSubscript->getImage());
+ m_writer->emit("atomic_fetch_add(");
emitAtomicImageCoord(imageSubscript);
isImageOp = true;
}
@@ -616,8 +641,8 @@ bool MetalSourceEmitter::tryEmitInstStmtImpl(IRInst* inst)
bool isImageOp = false;
if (auto imageSubscript = isTextureAccess(inst))
{
- emitOperand(imageSubscript->getImage(), getInfo(EmitOp::Postfix));
- m_writer->emit(".atomic_fetch_sub(");
+ emitImageOperandWithAccessor(imageSubscript->getImage());
+ m_writer->emit("atomic_fetch_sub(");
emitAtomicImageCoord(imageSubscript);
isImageOp = true;
}
diff --git a/source/slang/slang-emit-metal.h b/source/slang/slang-emit-metal.h
index eb63bd22e..e331234f7 100644
--- a/source/slang/slang-emit-metal.h
+++ b/source/slang/slang-emit-metal.h
@@ -97,6 +97,7 @@ protected:
bool _emitUserSemantic(UnownedStringSlice semanticName, IRIntegerValue semanticIndex);
bool maybeEmitSystemSemantic(IRInst* inst);
+ void emitImageOperandWithAccessor(IRInst* imageOperand);
void emitAtomicImageCoord(IRImageSubscript* subscript);
void emitAtomicDestOperand(IRInst* operand);
void emitAtomicSrcOperand(bool isImage, IRInst* operand);
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 18c54587a..7b1bd66d8 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -1500,12 +1500,30 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
m_addressingMode,
m_memoryModel);
+ // Emit OpSamplerImageAddressingModeNV if bindless texture capability is enabled
+ auto targetCaps = m_targetProgram->getTargetReq()->getTargetCaps();
+
+ if (targetCaps.implies(CapabilityAtom::spvBindlessTextureNV))
+ {
+ requireSPIRVCapability((SpvCapability)SpvCapabilityBindlessTextureNV);
+ requireSPIRVCapability(SpvCapabilityInt64); // Required for 64-bit addressing mode
+ ensureExtensionDeclaration(UnownedStringSlice("SPV_NV_bindless_texture"));
+
+ emitInstCustomOperandFunc(
+ getSection(SpvLogicalSectionID::MemoryModel),
+ nullptr,
+ SpvOpSamplerImageAddressingModeNV,
+ [&]()
+ {
+ emitOperand(SpvWord(64)); // 64-bit addressing mode
+ });
+ }
+
if (m_memoryModel == SpvMemoryModelVulkan)
{
requireSPIRVCapability(SpvCapabilityVulkanMemoryModel);
ensureExtensionDeclaration(UnownedStringSlice("SPV_KHR_vulkan_memory_model"));
- auto targetCaps = m_targetProgram->getTargetReq()->getTargetCaps();
if (targetCaps.implies(CapabilityAtom::spvVulkanMemoryModelDeviceScopeKHR))
{
requireSPIRVCapability(SpvCapabilityVulkanMemoryModelDeviceScope);
@@ -2129,7 +2147,25 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
{
IRBuilder builder(inst);
builder.setInsertBefore(inst);
- return emitOpTypeVector(inst, builder.getUIntType(), SpvLiteralInteger::from32(2));
+ auto targetCaps = m_targetProgram->getTargetReq()->getTargetCaps();
+
+ if (targetCaps.implies(CapabilityAtom::spvBindlessTextureNV))
+ {
+ // For spvBindlessTextureNV, DescriptorHandleType should be a uint64_t
+ // (OpTypeInt 64 0)
+ return emitOpTypeInt(
+ inst,
+ SpvLiteralInteger::from32(64),
+ SpvLiteralInteger::from32(0));
+ }
+ else
+ {
+ // For other targets, use uint2 (OpTypeVector of 2 uint32)
+ return emitOpTypeVector(
+ inst,
+ builder.getUIntType(),
+ SpvLiteralInteger::from32(2));
+ }
}
case kIROp_SubpassInputType:
return ensureSubpassInputType(inst, cast<IRSubpassInputType>(inst));
@@ -4154,12 +4190,59 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
case kIROp_CastDescriptorHandleToUInt2:
case kIROp_CastUInt2ToDescriptorHandle:
case kIROp_GlobalValueRef:
+ case kIROp_CastUInt64ToDescriptorHandle:
+ case kIROp_CastDescriptorHandleToUInt64:
{
auto inner = ensureInst(inst->getOperand(0));
registerInst(inst, inner);
result = inner;
break;
}
+ case kIROp_CastDescriptorHandleToResource:
+ // Convert DescriptorHandle (uint64_t handle) to appropriate resource type
+ {
+ auto targetCaps = m_targetProgram->getTargetReq()->getTargetCaps();
+
+ if (targetCaps.implies(CapabilityAtom::spvBindlessTextureNV))
+ {
+ requireSPIRVCapability((SpvCapability)SpvCapabilityBindlessTextureNV);
+ ensureExtensionDeclaration(UnownedStringSlice("SPV_NV_bindless_texture"));
+
+ auto operand = ensureInst(inst->getOperand(0));
+ SpvOp conversionOp = SpvOpConvertUToSampledImageNV;
+ IRType* resultType = inst->getDataType();
+
+ switch (resultType->getOp())
+ {
+ case kIROp_TextureType:
+ conversionOp = SpvOpConvertUToSampledImageNV;
+ result = emitInst(
+ parent,
+ inst,
+ conversionOp,
+ inst->getDataType(),
+ kResultID,
+ operand);
+ break;
+ case kIROp_SamplerStateType:
+ conversionOp = SpvOpConvertUToSamplerNV;
+ result = emitInst(
+ parent,
+ inst,
+ conversionOp,
+ inst->getDataType(),
+ kResultID,
+ operand);
+ break;
+ default:
+ // Unsupported result type for descriptor-to-resource conversion
+ SLANG_UNEXPECTED(
+ "Unsupported result type for CastDescriptorHandleToResource");
+ break;
+ }
+ }
+ break;
+ }
case kIROp_GetVulkanRayTracingPayloadLocation:
{
IRInst* location = getVulkanPayloadLocation(inst->getOperand(0));
diff --git a/source/slang/slang-ir-insts-stable-names.lua b/source/slang/slang-ir-insts-stable-names.lua
index 589c4b130..b2c216bb4 100644
--- a/source/slang/slang-ir-insts-stable-names.lua
+++ b/source/slang/slang-ir-insts-stable-names.lua
@@ -670,4 +670,6 @@ return {
["SPIRVAsmOperand.__imageType"] = 666,
["SPIRVAsmOperand.__sampledImageType"] = 667,
["Type.CLayout"] = 668,
+ ["CastUInt64ToDescriptorHandle"] = 669,
+ ["CastDescriptorHandleToUInt64"] = 670,
}
diff --git a/source/slang/slang-ir-insts.lua b/source/slang/slang-ir-insts.lua
index 6487f36d0..0ad02b87c 100644
--- a/source/slang/slang-ir-insts.lua
+++ b/source/slang/slang-ir-insts.lua
@@ -1891,6 +1891,8 @@ local insts = {
{ EnumCast = { min_operands = 1 } },
{ CastUInt2ToDescriptorHandle = { min_operands = 1 } },
{ CastDescriptorHandleToUInt2 = { min_operands = 1 } },
+ { CastUInt64ToDescriptorHandle = { min_operands = 1 } },
+ { CastDescriptorHandleToUInt64 = { min_operands = 1 } },
-- Represents a no-op cast to convert a resource pointer to a resource on targets where the resource handles are
-- already concrete types.
{ CastDescriptorHandleToResource = { min_operands = 1 } },
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index a61859a5e..e9d1a1199 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -8709,6 +8709,8 @@ bool IRInst::mightHaveSideEffects(SideEffectAnalysisOptions options)
case kIROp_EnumCast:
case kIROp_CastUInt2ToDescriptorHandle:
case kIROp_CastDescriptorHandleToUInt2:
+ case kIROp_CastUInt64ToDescriptorHandle:
+ case kIROp_CastDescriptorHandleToUInt64:
case kIROp_CastDescriptorHandleToResource:
case kIROp_GetDynamicResourceHeap:
case kIROp_CastDynamicResource:
diff --git a/tests/language-feature/descriptor-handle/desc-handle-nv-bindless-texture.slang b/tests/language-feature/descriptor-handle/desc-handle-nv-bindless-texture.slang
new file mode 100644
index 000000000..0f4dfb619
--- /dev/null
+++ b/tests/language-feature/descriptor-handle/desc-handle-nv-bindless-texture.slang
@@ -0,0 +1,64 @@
+//TEST:SIMPLE(filecheck=SAMPLER): -target spirv -capability spvBindlessTextureNV -stage compute -entry computeMain -DSAMPLER
+//TEST:SIMPLE(filecheck=COMBINED_IMAGE_SAMPLER): -target spirv -capability spvBindlessTextureNV -stage compute -entry computeMain -DCOMBINED_IMAGE_SAMPLER
+//TEST:SIMPLE(filecheck=SAMPLED_IMAGE): -target spirv -capability spvBindlessTextureNV -stage compute -entry computeMain -DSAMPLED_IMAGE
+//TEST:SIMPLE(filecheck=STORAGE_IMAGE): -target spirv -capability spvBindlessTextureNV -stage compute -entry computeMain -DSTORAGE_IMAGE
+
+[[vk::binding(0)]] RWTexture1D<float> t1;
+[[vk::binding(1)]] RWTexture1D<float> t2;
+[[vk::binding(2)]] RWTexture1D<float> t3;
+[[vk::binding(3)]] Texture1D<float> t4;
+
+#ifdef SAMPLER
+//SAMPLER: [[SType:%[0-9]+]] = OpTypeSampler
+//SAMPLER: OpConvertUToSamplerNV [[SType]]
+//SAMPLER: OpSampledImage
+uniform SamplerState.Handle sampler;
+#endif
+
+#ifdef COMBINED_IMAGE_SAMPLER
+//COMBINED_IMAGE_SAMPLER: [[SIType:%[0-9]+]] = OpTypeSampledImage
+//COMBINED_IMAGE_SAMPLER: OpConvertUToSampledImageNV [[SIType]]
+uniform Sampler1DShadow.Handle combinedSampler;
+#endif
+
+#ifdef SAMPLED_IMAGE
+//SAMPLED_IMAGE: [[IType0:%[0-9]+]] = OpTypeImage
+//SAMPLED_IMAGE: [[IType1:%[0-9]+]] = OpTypeImage
+//SAMPLED_IMAGE: OpConvertUToSampledImageNV [[IType1]]
+//SAMPLED_IMAGE: OpImageFetch
+uniform Texture1D<float>.Handle texture;
+#endif
+
+#ifdef STORAGE_IMAGE
+//STORAGE_IMAGE: OpConvertUToSampledImageNV
+//STORAGE_IMAGE: OpImageRead
+//STORAGE_IMAGE: OpConvertUToSampledImageNV
+//STORAGE_IMAGE: OpImageRead
+uniform RWTexture1D<float>.Handle rwTexture1;
+uniform RWTexture2D<float>.Handle rwTexture2;
+#endif
+
+[shader("compute")]
+[numthreads(8, 8, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ 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
+}
+
diff --git a/tests/metal/atomic-texture-buffer.slang b/tests/metal/atomic-texture-buffer.slang
index 9f24018c7..a8f909255 100644
--- a/tests/metal/atomic-texture-buffer.slang
+++ b/tests/metal/atomic-texture-buffer.slang
@@ -30,37 +30,37 @@ void test()
#endif //FLOAT
// buffer
-// METAL: .atomic_fetch_add
-// METAL: .atomic_fetch_and
-// METAL: .atomic_fetch_max
-// METAL: .atomic_fetch_min
-// METAL: .atomic_fetch_or
-// METAL: .atomic_fetch_xor
-// METAL: .atomic_fetch_add
-// METAL: .atomic_fetch_and
-// METAL: .atomic_fetch_max
-// METAL: .atomic_fetch_min
-// METAL: .atomic_fetch_or
-// METAL: .atomic_fetch_xor
-// METAL: .atomic_exchange
-// METAL: .atomic_compare_exchange_weak
-// METAL: .atomic_compare_exchange_weak
+// METAL: ->atomic_fetch_add
+// METAL: ->atomic_fetch_and
+// METAL: ->atomic_fetch_max
+// METAL: ->atomic_fetch_min
+// METAL: ->atomic_fetch_or
+// METAL: ->atomic_fetch_xor
+// METAL: ->atomic_fetch_add
+// METAL: ->atomic_fetch_and
+// METAL: ->atomic_fetch_max
+// METAL: ->atomic_fetch_min
+// METAL: ->atomic_fetch_or
+// METAL: ->atomic_fetch_xor
+// METAL: ->atomic_exchange
+// METAL: ->atomic_compare_exchange_weak
+// METAL: ->atomic_compare_exchange_weak
-// METAL: .atomic_fetch_add
-// METAL: .atomic_fetch_and
-// METAL: .atomic_fetch_max
-// METAL: .atomic_fetch_min
-// METAL: .atomic_fetch_or
-// METAL: .atomic_fetch_xor
-// METAL: .atomic_fetch_add
-// METAL: .atomic_fetch_and
-// METAL: .atomic_fetch_max
-// METAL: .atomic_fetch_min
-// METAL: .atomic_fetch_or
-// METAL: .atomic_fetch_xor
-// METAL: .atomic_exchange
-// METAL: .atomic_compare_exchange_weak
-// METAL: .atomic_compare_exchange_weak
+// METAL: ->atomic_fetch_add
+// METAL: ->atomic_fetch_and
+// METAL: ->atomic_fetch_max
+// METAL: ->atomic_fetch_min
+// METAL: ->atomic_fetch_or
+// METAL: ->atomic_fetch_xor
+// METAL: ->atomic_fetch_add
+// METAL: ->atomic_fetch_and
+// METAL: ->atomic_fetch_max
+// METAL: ->atomic_fetch_min
+// METAL: ->atomic_fetch_or
+// METAL: ->atomic_fetch_xor
+// METAL: ->atomic_exchange
+// METAL: ->atomic_compare_exchange_weak
+// METAL: ->atomic_compare_exchange_weak
InterlockedAdd(intBuffer[0], valInt);
InterlockedAnd(intBuffer[0], valInt);
InterlockedMax(intBuffer[0], valInt);