summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2025-01-10 10:57:04 -0800
committerGitHub <noreply@github.com>2025-01-10 10:57:04 -0800
commit5290c580632cfb56847b863a32dc020a21d1c93e (patch)
tree4c543f28d13f62a1dc3293b76151dda7585743ab /source/slang
parent4cfae806a6f9c0203ce44c4ce04df5ad66cdc8a2 (diff)
Initial implementation of SP#015 `DescriptorHandle<T>`. (#6028)
* Initial implementation of `ResourcePtr<T>`. * Update docs * Fix build error. * Add more discussion. * Update documentation. * Update TOC. * Fix. * Fix. * Add test case for custom `getResourceFromBindlessHandle`. * Add namehint to generated descriptor heap param. * Fix. * Fix. * format code * Rename to `DescriptorHandle`, and add `T.Handle` alias. * Fix compiler error. * Fix. * Fix build. * Renames. * Fix documentation. * Documentation fix. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/hlsl.meta.slang183
-rw-r--r--source/slang/slang-ast-type.h5
-rw-r--r--source/slang/slang-check-expr.cpp73
-rw-r--r--source/slang/slang-check-impl.h1
-rw-r--r--source/slang/slang-emit-c-like.cpp23
-rw-r--r--source/slang/slang-emit-c-like.h5
-rw-r--r--source/slang/slang-emit-cpp.h5
-rw-r--r--source/slang/slang-emit-hlsl.cpp18
-rw-r--r--source/slang/slang-emit-metal.h6
-rw-r--r--source/slang/slang-emit-spirv.cpp8
-rw-r--r--source/slang/slang-emit-wgsl.cpp7
-rw-r--r--source/slang/slang-emit-wgsl.h6
-rw-r--r--source/slang/slang-emit.cpp14
-rw-r--r--source/slang/slang-ir-float-non-uniform-resource-index.cpp219
-rw-r--r--source/slang/slang-ir-float-non-uniform-resource-index.h20
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp19
-rw-r--r--source/slang/slang-ir-inst-defs.h14
-rw-r--r--source/slang/slang-ir-insts.h2
-rw-r--r--source/slang/slang-ir-layout.cpp8
-rw-r--r--source/slang/slang-ir-legalize-types.cpp4
-rw-r--r--source/slang/slang-ir-lower-combined-texture-sampler.cpp68
-rw-r--r--source/slang/slang-ir-lower-dynamic-resource-heap.cpp104
-rw-r--r--source/slang/slang-ir-lower-dynamic-resource-heap.h13
-rw-r--r--source/slang/slang-ir-spirv-legalize.cpp127
-rw-r--r--source/slang/slang-ir.cpp4
-rw-r--r--source/slang/slang-ir.h5
-rw-r--r--source/slang/slang-lookup.cpp3
-rw-r--r--source/slang/slang-mangle.cpp13
-rw-r--r--source/slang/slang-options.cpp20
-rw-r--r--source/slang/slang-type-layout.cpp11
30 files changed, 812 insertions, 196 deletions
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 5fb82d875..7964e26d8 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -20932,14 +20932,38 @@ struct ConstBufferPointer
// new aliased bindings for each distinct cast type.
//
+/// Represent the kind of a descriptor type.
+enum DescriptorKind
+{
+ Unknown, /// Unknown descriptor kind.
+ Texture, /// A texture descriptor.
+ CombinedTextureSampler, /// A combined texture and sampler state descriptor.
+ Buffer, /// A buffer descriptor.
+ Sampler, /// A sampler state descriptor.
+ AccelerationStructure, /// A ray tracing acceleration structure descriptor.
+}
+
+/// Represents an opaque descriptor type, such as textures, samplers, and buffers etc,
+/// whose size may be undefined and can't be directly accessed as ordinary data.
+[sealed]
+[builtin]
+interface IOpaqueDescriptor
+{
+ /// The kind of the descriptor.
+ static const DescriptorKind kind;
+}
+
__magic_type(DynamicResourceType)
__intrinsic_type($(kIROp_DynamicResourceType))
struct __DynamicResource<let kind = __DynamicResourceKind.General>
{
__intrinsic_op($(kIROp_CastDynamicResource))
T as<T : __IDynamicResourceCastable<kind>>();
+
+ __intrinsic_op($(kIROp_CastDynamicResource))
+ T asOpaqueDescriptor<T : IOpaqueDescriptor>();
}
-interface __IDynamicResourceCastable<let kind = __DynamicResourceKind.General>
+interface __IDynamicResourceCastable<let kind = __DynamicResourceKind.General> : IOpaqueDescriptor
{
}
@@ -20955,41 +20979,166 @@ extension _Texture<T, Shape, isArray, isMS, sampleCount, access, isShadow, isCom
__intrinsic_op($(kIROp_CastDynamicResource))
__implicit_conversion($(kConversionCost_GenericParamUpcast))
__init(__DynamicResource res);
-}
-${{{{
-const char* kDynamicResourceCastableTypes[] = {
- "StructuredBuffer<T, L>", "RWStructuredBuffer<T, L>",
- "AppendStructuredBuffer<T, L>", "ConsumeStructuredBuffer<T, L>", "RasterizerOrderedStructuredBuffer<T, L>",
- "ByteAddressBuffer", "RWByteAddressBuffer", "RasterizerOrderedByteAddressBuffer",
+ typealias Handle = DescriptorHandle<This>;
+
+ static const DescriptorKind kind = isCombined!=0 ? DescriptorKind.CombinedTextureSampler : DescriptorKind.Texture;
- "SamplerState", "SamplerComparisonState",
+ __implicit_conversion($(kConversionCost_ImplicitDereference))
+ [ForceInline]
+ __init(DescriptorHandle<This> bindless)
+ {
+ return getDescriptorFromHandle(bindless);
+ }
+}
- "ConstantBuffer<T, L>", "TextureBuffer<T>",
+${{{{
+struct DynamicResourceTypeInfo
+{
+ const char* name;
+ const char* kind;
+ const char* dynamicKind;
};
-for (auto typeName : kDynamicResourceCastableTypes) {
- auto kind = strstr(typeName, "Sampler") ? "Sampler" : "General";
+const DynamicResourceTypeInfo kDynamicResourceCastableTypes[] = {
+ {"StructuredBuffer<T, L>", "Buffer", "General"},
+ {"RWStructuredBuffer<T, L>", "Buffer", "General"},
+ {"AppendStructuredBuffer<T, L>", "Buffer", "General"},
+ {"ConsumeStructuredBuffer<T, L>", "Buffer", "General"},
+ {"RasterizerOrderedStructuredBuffer<T, L>", "Buffer", "General"},
+ {"ByteAddressBuffer", "Buffer", "General"},
+ {"RWByteAddressBuffer", "Buffer", "General"},
+ {"RasterizerOrderedByteAddressBuffer", "Buffer", "General"},
+ {"SamplerState", "Sampler", "Sampler"},
+ {"SamplerComparisonState", "Sampler", "Sampler"},
+ {"ConstantBuffer<T, L>", "Buffer", "General"},
+ {"TextureBuffer<T>", "Buffer", "General"},
+ {"RaytracingAccelerationStructure", "AccelerationStructure", "General"},
+};
- if (strstr(typeName, "StructuredBuffer<T, L>"))
- sb << "__generic<T, L : IBufferDataLayout = DefaultDataLayout>\n";
- else if (strstr(typeName, "ConstantBuffer<T, L>"))
- sb << "__generic<T, L : IBufferDataLayout = DefaultDataLayout>\n";
- else if (strstr(typeName, "Buffer<T>"))
+for (auto type : kDynamicResourceCastableTypes) {
+ auto dynamicKind = type.dynamicKind;
+ auto kind = type.kind;
+ auto typeName = type.name;
+ if (strstr(typeName, "<T, L>"))
+ sb << "__generic<T, L : IBufferDataLayout>\n";
+ else if (strstr(typeName, "<T>"))
sb << "__generic<T>\n";
}}}}
-extension $(typeName) : __IDynamicResourceCastable<__DynamicResourceKind.$(kind)>
+extension $(typeName) : __IDynamicResourceCastable<__DynamicResourceKind.$(dynamicKind)>
{
__intrinsic_op($(kIROp_CastDynamicResource))
__implicit_conversion($(kConversionCost_GenericParamUpcast))
__init(__DynamicResource res);
+
+ static const DescriptorKind kind = DescriptorKind.$(kind);
+
+ typealias Handle = DescriptorHandle<$(typeName)>;
+
+ __implicit_conversion($(kConversionCost_ImplicitDereference))
+ [ForceInline]
+ __init(DescriptorHandle<$(typeName)> bindless)
+ {
+ return getDescriptorFromHandle(bindless);
+ }
}
${{{{
}
}}}}
+/// Represents a bindless resource handle. A bindless resource handle is always a concrete type and can be
+/// declared in any memory location.
+__magic_type(DescriptorHandleType)
+__intrinsic_type($(kIROp_DescriptorHandleType))
+struct DescriptorHandle<T:IOpaqueDescriptor> : IComparable
+{
+ [require(glsl_spirv)]
+ [require(hlsl, sm_6_6)]
+ [require(wgsl)]
+ __intrinsic_op($(kIROp_CastUInt2ToDescriptorHandle))
+ __init(uint2 handleValue);
+
+ [ForceInline]
+ bool equals(DescriptorHandle<T> other) { return all(__vectorEql((uint2)this, (uint2)other)); }
+
+ [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); }
+
+ [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); }
+}
+
+extension uint2
+{
+ __intrinsic_op($(kIROp_CastDescriptorHandleToUInt2))
+ [require(glsl_spirv)]
+ [require(hlsl, sm_6_6)]
+ [require(wgsl)]
+ __init<T:IOpaqueDescriptor>(DescriptorHandle<T> bindless);
+}
+
+__generic<T:IOpaqueDescriptor>
+[ForceInline]
+__prefix T operator*(DescriptorHandle<T> value)
+{
+ return getDescriptorFromHandle(value);
+}
+
+__intrinsic_op($(kIROp_GetDynamicResourceHeap))
+T[] __getDynamicResourceHeap<T:IOpaqueDescriptor>();
+
+
+// Used by the HLSL backend only, load a sampler state handle from SamplerDescriptorHeap.
+__intrinsic_op($(kIROp_LoadSamplerDescriptorFromHeap))
+T __loadSamplerDescriptorFromHeap<T>(uint index);
+
+// Used by the HLSL backend only, load a resource handle from ResourceDescriptorHeap.
+__intrinsic_op($(kIROp_LoadResourceDescriptorFromHeap))
+T __loadResourceDescriptorFromHeap<T>(uint index);
+
+// Used by the HLSL and Metal backends only,
+// create a combined texture sampler object from a bindless handle.
+__intrinsic_op($(kIROp_MakeCombinedTextureSamplerFromHandle))
+T __makeCombinedTextureSamplerFromHandle<T, U>(U handle);
+
+__intrinsic_op($(kIROp_CastDescriptorHandleToResource))
+T __castDescriptorHandleToResource<T:IOpaqueDescriptor>(DescriptorHandle<T> ptr);
+
+/// The default implementation of `getDescriptorFromHandle`, which converts from a descriptor handle
+/// to a descriptor object.
+[ForceInline]
+T defaultGetDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handleValue)
+{
+ __target_switch
+ {
+ case hlsl:
+ if (T.kind == DescriptorKind.Sampler)
+ return __loadSamplerDescriptorFromHeap<T>(((uint2)handleValue).x);
+ else if (T.kind == DescriptorKind.CombinedTextureSampler)
+ return __makeCombinedTextureSamplerFromHandle<T>((uint2)handleValue);
+ else
+ return __loadResourceDescriptorFromHeap<T>(((uint2)handleValue).x);
+ case spirv:
+ case glsl:
+ case wgsl:
+ return __getDynamicResourceHeap<T>()[((uint2)handleValue).x];
+ default:
+ return __castDescriptorHandleToResource<T>(handleValue);
+ }
+}
+
+/// Declaration of the `getDescriptorFromHandle` that the user code can provide to customize
+/// how a descriptor handle is converted into a actual descriptor.
+[ForceInline]
+extern T getDescriptorFromHandle<T:IOpaqueDescriptor>(DescriptorHandle<T> handleValue)
+{
+ return defaultGetDescriptorFromHandle(handleValue);
+}
+
+__intrinsic_op($(kIROp_NonUniformResourceIndex))
+DescriptorHandle<T> nonuniform<T:IOpaqueDescriptor>(DescriptorHandle<T> ptr);
__glsl_version(450)
__glsl_extension(GL_ARB_shader_clock)
diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h
index c5c5de5d2..9d4297919 100644
--- a/source/slang/slang-ast-type.h
+++ b/source/slang/slang-ast-type.h
@@ -394,6 +394,11 @@ class GLSLInputAttachmentType : public BuiltinType
};
+class DescriptorHandleType : public PointerLikeType
+{
+ SLANG_AST_CLASS(DescriptorHandleType)
+};
+
// Base class for types used when desugaring parameter block
// declarations, includeing HLSL `cbuffer` or GLSL `uniform` blocks.
class ParameterGroupType : public PointerLikeType
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 95d5a2a7c..ce6206973 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -511,24 +511,41 @@ DeclRefExpr* SemanticsVisitor::ConstructDeclRefExpr(
}
}
-Expr* SemanticsVisitor::ConstructDerefExpr(Expr* base, SourceLoc loc)
+Expr* SemanticsVisitor::constructDerefExpr(Expr* base, QualType elementType, SourceLoc loc)
{
- auto elementType = getPointedToTypeIfCanImplicitDeref(base->type);
- SLANG_ASSERT(elementType);
+ if (auto resPtrType = as<DescriptorHandleType>(base->type))
+ {
+ return coerce(CoercionSite::ExplicitCoercion, resPtrType->getElementType(), base);
+ }
auto derefExpr = m_astBuilder->create<DerefExpr>();
derefExpr->loc = loc;
derefExpr->base = base;
derefExpr->type = QualType(elementType);
- if (as<PtrType>(base->type))
+ if (as<PtrType>(base->type) || as<RefType>(base->type))
+ {
derefExpr->type.isLeftValue = true;
+ }
else
+ {
+ derefExpr->type.isLeftValue = base->type.isLeftValue;
derefExpr->type.isLeftValue = base->type.isLeftValue;
+ derefExpr->type.hasReadOnlyOnTarget = base->type.hasReadOnlyOnTarget;
+ derefExpr->type.isWriteOnly = base->type.isWriteOnly;
+ }
return derefExpr;
}
+Expr* SemanticsVisitor::ConstructDerefExpr(Expr* base, SourceLoc loc)
+{
+ auto elementType = getPointedToTypeIfCanImplicitDeref(base->type);
+ SLANG_ASSERT(elementType);
+
+ return constructDerefExpr(base, elementType, loc);
+}
+
InvokeExpr* SemanticsVisitor::constructUncheckedInvokeExpr(
Expr* callee,
const List<Expr*>& arguments)
@@ -2301,8 +2318,7 @@ Expr* SemanticsVisitor::CheckSimpleSubscriptExpr(IndexExpr* subscriptExpr, Type*
auto indexExpr = subscriptExpr->indexExprs[0];
- if (!indexExpr->type->equals(m_astBuilder->getIntType()) &&
- !indexExpr->type->equals(m_astBuilder->getUIntType()))
+ if (!isScalarIntegerType(indexExpr->type.type))
{
getSink()->diagnose(indexExpr, Diagnostics::subscriptIndexNonInteger);
return CreateErrorExpr(subscriptExpr);
@@ -4084,43 +4100,16 @@ Expr* SemanticsVisitor::maybeDereference(Expr* inExpr, CheckBaseContext checkBas
for (;;)
{
auto baseType = expr->type;
- QualType elementType;
- if (auto pointerLikeType = as<PointerLikeType>(baseType))
- {
- elementType = QualType(pointerLikeType->getElementType());
- elementType.isLeftValue = baseType.isLeftValue;
- elementType.hasReadOnlyOnTarget = baseType.hasReadOnlyOnTarget;
- elementType.isWriteOnly = baseType.isWriteOnly;
- }
- else if (auto ptrType = as<PtrType>(baseType))
+ if (as<PtrType>(baseType))
{
if (checkBaseContext == CheckBaseContext::Subscript)
return expr;
- elementType = QualType(ptrType->getValueType());
- elementType.isLeftValue = true;
}
- else
- {
- auto newExpr = maybeOpenRef(expr);
- if (newExpr != expr)
- {
- expr = newExpr;
- continue;
- }
- }
- if (elementType.type)
- {
- auto derefExpr = m_astBuilder->create<DerefExpr>();
- derefExpr->base = expr;
- derefExpr->type = elementType;
-
- expr = derefExpr;
- continue;
- }
- break;
+ auto elementType = getPointedToTypeIfCanImplicitDeref(baseType);
+ if (!elementType)
+ return expr;
+ expr = constructDerefExpr(expr, elementType, inExpr->loc);
}
- // Default case: just use the expression as-is
- return expr;
}
Expr* SemanticsVisitor::CheckMatrixSwizzleExpr(
@@ -4740,8 +4729,12 @@ Expr* SemanticsVisitor::_lookupStaticMember(DeclRefExpr* expr, Expr* baseExpress
handleLeafCase(nsType->getDeclRef(), nsType);
else if (auto aggType = as<DeclRefType>(e->type))
handleLeafCase(aggType->getDeclRef(), aggType);
- else if (auto typetype = as<TypeType>(e->type))
- handleLeafCase(DeclRef<Decl>(), typetype->getType());
+ else if (as<TypeType>(e->type))
+ {
+ auto properType = CoerceToProperType(TypeExp(e));
+ if (properType.type)
+ handleLeafCase(DeclRef<Decl>(), properType.type);
+ }
};
auto& baseType = baseExpression->type;
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 300596caa..b3e30dbc2 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -1268,6 +1268,7 @@ public:
Expr* originalExpr);
Expr* ConstructDerefExpr(Expr* base, SourceLoc loc);
+ Expr* constructDerefExpr(Expr* base, QualType elementType, SourceLoc loc);
InvokeExpr* constructUncheckedInvokeExpr(Expr* callee, const List<Expr*>& arguments);
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index b63b9118e..7b51495e2 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -391,6 +391,23 @@ void CLikeSourceEmitter::_emitType(IRType* type, DeclaratorInfo* declarator)
_emitType(rateQualifiedType->getValueType(), declarator);
}
break;
+ case kIROp_DescriptorHandleType:
+ {
+ // If the T is already bindless for target, emit it directly.
+ auto resPtrType = cast<IRDescriptorHandleType>(type);
+ if (isResourceTypeBindless(resPtrType->getResourceType()))
+ _emitType(resPtrType->getResourceType(), declarator);
+ else
+ {
+ // Otherwise, emit the DescriptorHandle<T> as uint2.
+ IRBuilder builder(resPtrType);
+ builder.setInsertBefore(resPtrType);
+ emitSimpleTypeAndDeclarator(
+ builder.getVectorType(builder.getUIntType(), 2),
+ declarator);
+ }
+ }
+ break;
case kIROp_ArrayType:
{
@@ -2568,7 +2585,11 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
emitOperand(inst->getOperand(1), rightSide(outerPrec, prec));
break;
}
-
+ case kIROp_CastDescriptorHandleToUInt2:
+ case kIROp_CastUInt2ToDescriptorHandle:
+ case kIROp_CastDescriptorHandleToResource:
+ emitOperand(inst->getOperand(0), outerPrec);
+ break;
// Binary ops
case kIROp_Add:
case kIROp_Sub:
diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h
index dd8e27674..e5080f731 100644
--- a/source/slang/slang-emit-c-like.h
+++ b/source/slang/slang-emit-c-like.h
@@ -512,6 +512,11 @@ protected:
virtual void emitGlobalParamDefaultVal(IRGlobalParam* inst) { SLANG_UNUSED(inst); }
virtual void emitPostDeclarationAttributesForType(IRInst* type) { SLANG_UNUSED(type); }
virtual bool doesTargetSupportPtrTypes() { return false; }
+ virtual bool isResourceTypeBindless(IRType* type)
+ {
+ SLANG_UNUSED(type);
+ return false;
+ }
virtual void emitLayoutSemanticsImpl(
IRInst* inst,
char const* uniformSemanticSpelling,
diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h
index 2a1114afc..8d4f0d49e 100644
--- a/source/slang/slang-emit-cpp.h
+++ b/source/slang/slang-emit-cpp.h
@@ -44,6 +44,11 @@ public:
protected:
// Implement CLikeSourceEmitter interface
+ virtual bool isResourceTypeBindless(IRType* type) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(type);
+ return true;
+ }
virtual bool doesTargetSupportPtrTypes() SLANG_OVERRIDE { return true; }
virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type)
SLANG_OVERRIDE;
diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp
index 83eec17b4..7bd3bb3db 100644
--- a/source/slang/slang-emit-hlsl.cpp
+++ b/source/slang/slang-emit-hlsl.cpp
@@ -903,6 +903,24 @@ bool HLSLSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOu
return true;
}
+ case kIROp_LoadSamplerDescriptorFromHeap:
+ {
+ emitType(inst->getDataType());
+ m_writer->emit("(");
+ m_writer->emit("SamplerDescriptorHeap[");
+ emitOperand(inst->getOperand(0), getInfo(EmitOp::General));
+ m_writer->emit("])");
+ return true;
+ }
+ case kIROp_LoadResourceDescriptorFromHeap:
+ {
+ emitType(inst->getDataType());
+ m_writer->emit("(");
+ m_writer->emit("ResourceDescriptorHeap[");
+ emitOperand(inst->getOperand(0), getInfo(EmitOp::General));
+ m_writer->emit("])");
+ return true;
+ }
case kIROp_ByteAddressBufferLoad:
{
// HLSL byte-address buffers have two kinds of `Load` operations.
diff --git a/source/slang/slang-emit-metal.h b/source/slang/slang-emit-metal.h
index b67a5b801..eb63bd22e 100644
--- a/source/slang/slang-emit-metal.h
+++ b/source/slang/slang-emit-metal.h
@@ -25,6 +25,12 @@ public:
protected:
RefPtr<MetalExtensionTracker> m_extensionTracker;
+ virtual bool isResourceTypeBindless(IRType* type) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(type);
+ return true;
+ }
+
void emitMemoryOrderOperand(IRInst* inst);
virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type)
SLANG_OVERRIDE;
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 1407404ad..068e1563c 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -1682,6 +1682,12 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
registerInst(inst, result);
return result;
}
+ case kIROp_DescriptorHandleType:
+ {
+ IRBuilder builder(inst);
+ builder.setInsertBefore(inst);
+ return emitOpTypeVector(inst, builder.getUIntType(), SpvLiteralInteger::from32(2));
+ }
case kIROp_SubpassInputType:
return ensureSubpassInputType(inst, cast<IRSubpassInputType>(inst));
case kIROp_TextureType:
@@ -3457,6 +3463,8 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
case kIROp_Lsh:
result = emitArithmetic(parent, inst);
break;
+ case kIROp_CastDescriptorHandleToUInt2:
+ case kIROp_CastUInt2ToDescriptorHandle:
case kIROp_GlobalValueRef:
{
auto inner = ensureInst(inst->getOperand(0));
diff --git a/source/slang/slang-emit-wgsl.cpp b/source/slang/slang-emit-wgsl.cpp
index f9181a50d..3b2cf12d0 100644
--- a/source/slang/slang-emit-wgsl.cpp
+++ b/source/slang/slang-emit-wgsl.cpp
@@ -557,6 +557,13 @@ void WGSLSourceEmitter::emitSimpleTypeImpl(IRType* type)
m_writer->emit(">");
return;
}
+ case kIROp_UnsizedArrayType:
+ {
+ m_writer->emit("array<");
+ emitType((IRType*)type->getOperand(0));
+ m_writer->emit(">");
+ return;
+ }
case kIROp_TextureType:
if (auto texType = as<IRTextureType>(type))
{
diff --git a/source/slang/slang-emit-wgsl.h b/source/slang/slang-emit-wgsl.h
index 390ee876d..4e0c18821 100644
--- a/source/slang/slang-emit-wgsl.h
+++ b/source/slang/slang-emit-wgsl.h
@@ -12,7 +12,11 @@ public:
: CLikeSourceEmitter(desc)
{
}
-
+ virtual bool isResourceTypeBindless(IRType* type) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(type);
+ return true;
+ }
virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type)
SLANG_OVERRIDE;
virtual void emitEntryPointAttributesImpl(
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index cd1b177b2..ee2582267 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -43,6 +43,7 @@
#include "slang-ir-explicit-global-context.h"
#include "slang-ir-explicit-global-init.h"
#include "slang-ir-fix-entrypoint-callsite.h"
+#include "slang-ir-float-non-uniform-resource-index.h"
#include "slang-ir-fuse-satcoop.h"
#include "slang-ir-glsl-legalize.h"
#include "slang-ir-glsl-liveness.h"
@@ -64,6 +65,7 @@
#include "slang-ir-lower-bit-cast.h"
#include "slang-ir-lower-buffer-element-type.h"
#include "slang-ir-lower-combined-texture-sampler.h"
+#include "slang-ir-lower-dynamic-resource-heap.h"
#include "slang-ir-lower-generics.h"
#include "slang-ir-lower-glsl-ssbo-types.h"
#include "slang-ir-lower-l-value-cast.h"
@@ -316,6 +318,7 @@ struct RequiredLoweringPassSet
bool glslSSBO;
bool byteAddressBuffer;
bool dynamicResource;
+ bool dynamicResourceHeap;
bool resolveVaryingInputRef;
};
@@ -426,6 +429,9 @@ void calcRequiredLoweringPassSet(
case kIROp_DynamicResourceType:
result.dynamicResource = true;
break;
+ case kIROp_GetDynamicResourceHeap:
+ result.dynamicResourceHeap = true;
+ break;
case kIROp_ResolveVaryingInputRef:
result.resolveVaryingInputRef = true;
break;
@@ -1122,6 +1128,9 @@ Result linkAndOptimizeIR(
else
simplifyIR(targetProgram, irModule, fastIRSimplificationOptions, sink);
+ if (requiredLoweringPassSet.dynamicResourceHeap)
+ lowerDynamicResourceHeap(targetProgram, irModule, sink);
+
#if 0
dumpIRIfEnabled(codeGenContext, irModule, "AFTER SSA");
#endif
@@ -1391,6 +1400,11 @@ Result linkAndOptimizeIR(
break;
}
+ if (!isSPIRV(targetRequest->getTarget()))
+ {
+ floatNonUniformResourceIndex(irModule, NonUniformResourceIndexFloatMode::Textual);
+ }
+
// Legalize non struct parameters that are expected to be structs for HLSL.
if (isD3DTarget(targetRequest))
legalizeNonStructParameterToStructForHLSL(irModule);
diff --git a/source/slang/slang-ir-float-non-uniform-resource-index.cpp b/source/slang/slang-ir-float-non-uniform-resource-index.cpp
new file mode 100644
index 000000000..e37b0445b
--- /dev/null
+++ b/source/slang/slang-ir-float-non-uniform-resource-index.cpp
@@ -0,0 +1,219 @@
+#include "slang-ir-float-non-uniform-resource-index.h"
+
+#include "slang-ir-util.h"
+
+namespace Slang
+{
+void processNonUniformResourceIndex(
+ IRInst* nonUniformResourceIndexInst,
+ NonUniformResourceIndexFloatMode floatMode)
+{
+ // float `NonUniformResourceIndex()` to right before the access operation
+ // by walking up the use-def chain
+ // from nonUniformResource inst of an index to an array of buffer or
+ // texture def all the way to the leaf operations. To be precise:
+ // - go through GEP and see if it calls an intrinsic function,
+ // then decorate the address itself (GetElementPtr)
+ // - go through GEP to identify the pointer access and the Loads that it
+ // accesses (GetElementPtr -> Load), then decorate the load instruction.
+ // - go through IntCasts to deal with u32 -> i32 / vice-versa (IntCast)
+ List<IRInst*> resWorkList;
+
+ // Handle cases when `nonUniformResourceIndexInst` inst is wrapped around
+ // an index in a nested fashion, i.e. nonUniform(nonUniform(index)) by
+ // only adding the inner-most inst in the worklist, and work our way out.
+ auto insti = nonUniformResourceIndexInst;
+ while (insti->getOp() == kIROp_NonUniformResourceIndex)
+ {
+ if (resWorkList.getCount() != 0)
+ resWorkList.removeLast();
+ resWorkList.add(insti);
+ insti = insti->getOperand(0);
+ }
+
+ // For all the users of a `nonUniformResourceIndexInst`, make them directly
+ // use the underlying base inst that is wrapped by `nonUniformResourceIndex`
+ // and finally wrap them with a `nonUniformResourceIndex`, and add back to the
+ // worklist, and keep bubbling them up until it can.
+ for (Index i = 0; i < resWorkList.getCount(); i++)
+ {
+ auto inst = resWorkList[i];
+ traverseUses(
+ inst,
+ [&](IRUse* use)
+ {
+ auto user = use->getUser();
+ IRBuilder builder(user);
+ builder.setInsertBefore(user);
+
+ IRInst* newUser = nullptr;
+ switch (user->getOp())
+ {
+ case kIROp_IntCast:
+ // Replace intCast(nonUniformRes(x)), into nonUniformRes(intCast(x))
+ newUser = builder.emitCast(user->getFullType(), inst->getOperand(0));
+ break;
+ case kIROp_CastDescriptorHandleToUInt2:
+ {
+ // Replace castBindlessToInt(nonUniformRes(x)), into
+ // nonUniformRes(castBindlessToInt(x))
+ auto operand = inst->getOperand(0);
+ newUser = builder.emitIntrinsicInst(
+ user->getFullType(),
+ kIROp_CastDescriptorHandleToUInt2,
+ 1,
+ &operand);
+ }
+ break;
+ case kIROp_GetElementPtr:
+ // Ignore when `NonUniformResourceIndex` is not on the index
+ if (floatMode != NonUniformResourceIndexFloatMode::SPIRV)
+ break;
+ if (user->getOperand(1) == inst)
+ {
+ // Replace gep(pArray, nonUniformRes(x)), into
+ // nonUniformRes(gep(pArray, x))
+ newUser = builder.emitElementAddress(
+ user->getFullType(),
+ user->getOperand(0),
+ inst->getOperand(0));
+ }
+ break;
+ case kIROp_GetElement:
+ // Ignore when `NonUniformResourceIndex` is not on base
+ if (user->getOperand(0) == inst)
+ {
+ // Replace getElement(nonuniformRes(obj), i), into
+ // nonUniformRes(getElement(obj, i))
+ newUser = builder.emitElementExtract(
+ user->getFullType(),
+ inst->getOperand(0),
+ user->getOperand(1));
+ }
+ break;
+ case kIROp_swizzle:
+ // Ignore when `NonUniformResourceIndex` is not on base
+ if (user->getOperand(0) == inst)
+ {
+ // Replace getElement(nonuniformRes(obj), i), into
+ // nonUniformRes(getElement(obj, i))
+ ShortList<IRInst*> operands;
+ for (UInt i = 0; i < user->getOperandCount(); i++)
+ operands.add(user->getOperand(i));
+ operands[0] = inst->getOperand(0);
+ newUser = builder.emitIntrinsicInst(
+ user->getFullType(),
+ kIROp_swizzle,
+ operands.getCount(),
+ operands.getArrayView().getBuffer());
+ }
+ break;
+ case kIROp_NonUniformResourceIndex:
+ // Replace nonUniformRes(nonUniformRes(x)), into nonUniformRes(x)
+ newUser = inst->getOperand(0);
+ break;
+ case kIROp_Load:
+ if (floatMode != NonUniformResourceIndexFloatMode::SPIRV)
+ break;
+ // Replace load(nonUniformRes(x)), into nonUniformRes(load(x))
+ newUser = builder.emitLoad(user->getFullType(), inst->getOperand(0));
+ break;
+ default:
+ // Ignore for all other unknown insts.
+ break;
+ };
+
+ // Early exit when we could not process the `NonUniformResourceIndex` inst.
+ if (!newUser)
+ return;
+
+ auto nonuniformUser = builder.emitNonUniformResourceIndexInst(newUser);
+ user->replaceUsesWith(nonuniformUser);
+
+ // Update the worklist with the newly added `NonUniformResourceIndex` inst,
+ // based on the base inst it was constructed around, in case we need to further
+ // bubble up the `NonUniformResourceIndex` inst.
+ switch (user->getOp())
+ {
+ case kIROp_IntCast:
+ case kIROp_GetElementPtr:
+ case kIROp_Load:
+ case kIROp_NonUniformResourceIndex:
+ case kIROp_CastDescriptorHandleToUInt2:
+ case kIROp_GetElement:
+ case kIROp_swizzle:
+ resWorkList.add(nonuniformUser);
+ break;
+ };
+
+ // Clean up the base inst from the IR module, to avoid duplicate decorations.
+ user->removeAndDeallocate();
+ });
+ }
+
+ if (floatMode != NonUniformResourceIndexFloatMode::SPIRV)
+ return;
+ // Once all the `NonUniformResourceIndex` insts are visited, and the inst type is bubbled up
+ // to the parent, a decoration is added to the operands of the insts.
+ for (int i = 0; i < resWorkList.getCount(); ++i)
+ {
+ // It is only required to decorate the base inst, if the `NonUniformResourceIndex` inst
+ // around it has any active uses.
+ auto inst = resWorkList[i];
+ if (!inst->hasUses())
+ {
+ inst->removeAndDeallocate();
+ continue;
+ }
+ // For each of the `NonUniformResourceIndex` inst that remain, decorate the base inst
+ // with a [NonUniformResource] decoration, which is the operand0 of the inst, only
+ // when the type is a resource type, or a pointer to a resource type, or a pointer
+ // in the Physical Storage buffer address space.
+ auto operand = inst->getOperand(0);
+ auto type = operand->getDataType();
+ if (isResourceType(type) || isPointerToResourceType(type))
+ {
+ IRBuilder builder(operand);
+ builder.addSPIRVNonUniformResourceDecoration(operand);
+ if (operand->getOp() == kIROp_Load)
+ {
+ // If the inst is a load, then the addr inst itself should also be decorated
+ // with the [NonUniformResource] decoration.
+ auto addr = operand->getOperand(0);
+ if (!addr->findDecoration<IRSPIRVNonUniformResourceDecoration>())
+ builder.addSPIRVNonUniformResourceDecoration(addr);
+ }
+ }
+ inst->replaceUsesWith(operand);
+ inst->removeAndDeallocate();
+ }
+}
+
+void floatNonUniformResourceIndex(IRModule* module, NonUniformResourceIndexFloatMode floatMode)
+{
+ // Walk through all the instructions in the module, and float the `NonUniformResourceIndex`
+ // insts to the right place in the IR module.
+
+ List<IRInst*> workList;
+ for (auto globalInst : module->getGlobalInsts())
+ {
+ auto func = as<IRGlobalValueWithCode>(getGenericReturnVal(globalInst));
+ if (!func)
+ continue;
+ workList.clear();
+ for (auto block : func->getBlocks())
+ {
+ for (auto inst : block->getChildren())
+ {
+ if (inst->getOp() == kIROp_NonUniformResourceIndex)
+ workList.add(inst);
+ }
+ }
+ for (auto inst : workList)
+ {
+ if (inst->getParent() != nullptr)
+ processNonUniformResourceIndex(inst, floatMode);
+ }
+ }
+}
+} // namespace Slang
diff --git a/source/slang/slang-ir-float-non-uniform-resource-index.h b/source/slang/slang-ir-float-non-uniform-resource-index.h
new file mode 100644
index 000000000..1f79b2378
--- /dev/null
+++ b/source/slang/slang-ir-float-non-uniform-resource-index.h
@@ -0,0 +1,20 @@
+#pragma once
+
+namespace Slang
+{
+struct IRInst;
+struct IRModule;
+
+enum class NonUniformResourceIndexFloatMode
+{
+ Textual,
+ SPIRV,
+};
+
+void processNonUniformResourceIndex(
+ IRInst* nonUniformResourceIndexInst,
+ NonUniformResourceIndexFloatMode floatMode);
+
+void floatNonUniformResourceIndex(IRModule* module, NonUniformResourceIndexFloatMode floatMode);
+
+} // namespace Slang
diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp
index 39f970319..7a46f45b4 100644
--- a/source/slang/slang-ir-glsl-legalize.cpp
+++ b/source/slang/slang-ir-glsl-legalize.cpp
@@ -4038,7 +4038,24 @@ void legalizeDispatchMeshPayloadForGLSL(IRModule* module)
void legalizeDynamicResourcesForGLSL(CodeGenContext* context, IRModule* module)
{
- List<IRGlobalParam*> toRemove;
+ List<IRInst*> toRemove;
+
+ // At this stage, we can safely remove the generic `getDescriptorFromHandle` function
+ // despite it being marked `export`.
+ for (auto inst : module->getGlobalInsts())
+ {
+ if (auto genFunc = as<IRGeneric>(inst))
+ {
+ if (!genFunc->hasUses())
+ {
+ toRemove.add(genFunc);
+ }
+ }
+ }
+ for (auto inst : toRemove)
+ {
+ inst->removeAndDeallocate();
+ }
for (auto inst : module->getGlobalInsts())
{
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 38e5f8869..f5af73dfa 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -133,6 +133,10 @@ INST(Nop, nop, 0, 0)
INST(ComPtrType, ComPtr, 1, HOISTABLE)
// A NativePtr<T> type represents a native pointer to a managed resource.
INST(NativePtrType, NativePtr, 1, HOISTABLE)
+
+ // A DescriptorHandle<T> type represents a bindless handle to an opaue resource type.
+ INST(DescriptorHandleType, DescriptorHandle, 1, HOISTABLE)
+
// An AtomicUint is a placeholder type for a storage buffer, and will be mangled during compiling.
INST(GLSLAtomicUintType, GLSLAtomicUint, 0, HOISTABLE)
@@ -365,6 +369,9 @@ INST(MakeTargetTuple, makeTuple, 0, 0)
INST(MakeValuePack, makeValuePack, 0, 0)
INST(GetTargetTupleElement, getTargetTupleElement, 0, 0)
INST(GetTupleElement, getTupleElement, 2, 0)
+INST(LoadResourceDescriptorFromHeap, LoadResourceDescriptorFromHeap, 1, 0)
+INST(LoadSamplerDescriptorFromHeap, LoadSamplerDescriptorFromHeap, 1, 0)
+INST(MakeCombinedTextureSamplerFromHandle, MakeCombinedTextureSamplerFromHandle, 1, 0)
INST(MakeWitnessPack, MakeWitnessPack, 0, HOISTABLE)
INST(Expand, Expand, 1, 0)
INST(Each, Each, 1, HOISTABLE)
@@ -1185,6 +1192,12 @@ INST(CastPtrToInt, CastPtrToInt, 1, 0)
INST(CastIntToPtr, CastIntToPtr, 1, 0)
INST(CastToVoid, castToVoid, 1, 0)
INST(PtrCast, PtrCast, 1, 0)
+INST(CastUInt2ToDescriptorHandle, CastUInt2ToDescriptorHandle, 1, 0)
+INST(CastDescriptorHandleToUInt2, CastDescriptorHandleToUInt2, 1, 0)
+
+// Represents a no-op cast to convert a resource pointer to a resource on targets where the resource handles are already concrete types.
+INST(CastDescriptorHandleToResource, CastDescriptorHandleToResource, 1, 0)
+
INST(TreatAsDynamicUniform, TreatAsDynamicUniform, 1, 0)
INST(SizeOf, sizeOf, 1, 0)
@@ -1201,6 +1214,7 @@ INST(IsHalf, IsHalf, 1, 0)
INST(IsUnsignedInt, IsUnsignedInt, 1, 0)
INST(IsSignedInt, IsSignedInt, 1, 0)
INST(IsVector, IsVector, 1, 0)
+INST(GetDynamicResourceHeap, GetDynamicResourceHeap, 0, HOISTABLE)
INST(ForwardDifferentiate, ForwardDifferentiate, 1, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index a288bca97..a58c2e900 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -364,7 +364,7 @@ struct IRSPIRVNonUniformResourceDecoration : IRDecoration
{
kOp = kIROp_SPIRVNonUniformResourceDecoration
};
- IR_LEAF_ISA(RequireGLSLVersionDecoration)
+ IR_LEAF_ISA(SPIRVNonUniformResourceDecoration)
IRConstant* getSPIRVNonUniformResourceOperand() { return cast<IRConstant>(getOperand(0)); }
IntegerLiteralValue getSPIRVNonUniformResource()
diff --git a/source/slang/slang-ir-layout.cpp b/source/slang/slang-ir-layout.cpp
index 8180ea6aa..1332c8a25 100644
--- a/source/slang/slang-ir-layout.cpp
+++ b/source/slang/slang-ir-layout.cpp
@@ -355,6 +355,14 @@ static Result _calcSizeAndAlignment(
case kIROp_DefaultBufferLayoutType:
*outSizeAndAlignment = IRSizeAndAlignment(0, 4);
return SLANG_OK;
+ case kIROp_DescriptorHandleType:
+ {
+ IRBuilder builder(type);
+ builder.setInsertBefore(type);
+ auto uintType = builder.getUIntType();
+ auto uint2Type = builder.getVectorType(uintType, 2);
+ return getSizeAndAlignment(optionSet, rules, uint2Type, outSizeAndAlignment);
+ }
case kIROp_AttributedType:
{
auto attributedType = cast<IRAttributedType>(type);
diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp
index 962514b08..ae95ca8da 100644
--- a/source/slang/slang-ir-legalize-types.cpp
+++ b/source/slang/slang-ir-legalize-types.cpp
@@ -2088,7 +2088,9 @@ static LegalVal legalizeInst(
case kIROp_Return:
result = legalizeRetVal(context, args[0], (IRReturn*)inst);
break;
-
+ case kIROp_CastDescriptorHandleToResource:
+ result = LegalVal::simple(inst);
+ break;
case kIROp_DebugVar:
result = legalizeDebugVar(context, type, (IRDebugVar*)inst);
break;
diff --git a/source/slang/slang-ir-lower-combined-texture-sampler.cpp b/source/slang/slang-ir-lower-combined-texture-sampler.cpp
index 20aab1291..7936dadad 100644
--- a/source/slang/slang-ir-lower-combined-texture-sampler.cpp
+++ b/source/slang/slang-ir-lower-combined-texture-sampler.cpp
@@ -19,6 +19,7 @@ struct LoweredCombinedSamplerStructInfo
struct LowerCombinedSamplerContext
{
Dictionary<IRType*, LoweredCombinedSamplerStructInfo> mapTypeToLoweredInfo;
+ Dictionary<IRType*, LoweredCombinedSamplerStructInfo> mapLoweredTypeToLoweredInfo;
CodeGenTarget codeGenTarget;
LoweredCombinedSamplerStructInfo lowerCombinedTextureSamplerType(IRTextureTypeBase* textureType)
@@ -96,6 +97,7 @@ struct LowerCombinedSamplerContext
builder.addLayoutDecoration(structType, info.typeLayout);
mapTypeToLoweredInfo.add(textureType, info);
+ mapLoweredTypeToLoweredInfo.add(info.type, info);
return info;
}
};
@@ -182,10 +184,8 @@ void lowerCombinedTextureSamplers(
continue;
for (auto block : func->getBlocks())
{
- IRInst* nextInst = nullptr;
- for (auto inst = block->getFirstInst(); inst; inst = nextInst)
+ for (auto inst : block->getModifiableChildren())
{
- nextInst = inst->getNextInst();
switch (inst->getOp())
{
case kIROp_CombinedTextureSamplerGetTexture:
@@ -195,6 +195,9 @@ void lowerCombinedTextureSamplers(
auto loweredInfo =
context.mapTypeToLoweredInfo.tryGetValue(combinedSamplerType);
if (!loweredInfo)
+ loweredInfo = context.mapLoweredTypeToLoweredInfo.tryGetValue(
+ combinedSamplerType);
+ if (!loweredInfo)
continue;
builder.setInsertBefore(inst);
auto fieldExtract = builder.emitFieldExtract(
@@ -207,6 +210,65 @@ void lowerCombinedTextureSamplers(
inst->removeAndDeallocate();
}
break;
+ case kIROp_CastDescriptorHandleToResource:
+ {
+ auto handle = inst->getOperand(0);
+ if (as<IRDescriptorHandleType>(handle->getDataType()))
+ {
+ // If handle is still a DescriptorHandle, we are on a target that
+ // where native resource handles are already bindless, e.g. metal.
+ // On these platforms, the handle is a struct containing texture
+ // and sampler fields, so we just need to insert the extract operations.
+ auto combinedSamplerType = inst->getDataType();
+ auto loweredInfo =
+ context.mapTypeToLoweredInfo.tryGetValue(combinedSamplerType);
+ if (!loweredInfo)
+ continue;
+ builder.setInsertBefore(inst);
+ auto textureVal = builder.emitFieldExtract(
+ loweredInfo->textureType,
+ handle,
+ loweredInfo->texture);
+ auto samplerVal = builder.emitFieldExtract(
+ loweredInfo->samplerType,
+ handle,
+ loweredInfo->sampler);
+ IRInst* args[] = {textureVal, samplerVal};
+ auto combinedSampler =
+ builder.emitMakeStruct(loweredInfo->type, 2, args);
+ inst->replaceUsesWith(combinedSampler);
+ inst->removeAndDeallocate();
+ }
+ }
+ break;
+
+ case kIROp_MakeCombinedTextureSamplerFromHandle:
+ {
+ auto combinedSamplerType = inst->getDataType();
+ auto loweredInfo =
+ context.mapTypeToLoweredInfo.tryGetValue(combinedSamplerType);
+ if (!loweredInfo)
+ continue;
+ auto handle = inst->getOperand(0);
+ builder.setInsertBefore(inst);
+ auto textureIndex = builder.emitElementExtract(handle, IRIntegerValue(0));
+ auto texture = builder.emitIntrinsicInst(
+ loweredInfo->textureType,
+ kIROp_LoadResourceDescriptorFromHeap,
+ 1,
+ &textureIndex);
+ auto samplerIndex = builder.emitElementExtract(handle, IRIntegerValue(1));
+ auto sampler = builder.emitIntrinsicInst(
+ loweredInfo->samplerType,
+ kIROp_LoadSamplerDescriptorFromHeap,
+ 1,
+ &samplerIndex);
+ IRInst* args[] = {texture, sampler};
+ auto combinedSampler = builder.emitMakeStruct(loweredInfo->type, 2, args);
+ inst->replaceUsesWith(combinedSampler);
+ inst->removeAndDeallocate();
+ }
+ break;
}
}
}
diff --git a/source/slang/slang-ir-lower-dynamic-resource-heap.cpp b/source/slang/slang-ir-lower-dynamic-resource-heap.cpp
new file mode 100644
index 000000000..60ec688a0
--- /dev/null
+++ b/source/slang/slang-ir-lower-dynamic-resource-heap.cpp
@@ -0,0 +1,104 @@
+#include "slang-ir-lower-dynamic-resource-heap.h"
+
+#include "compiler-core/slang-artifact-associated-impl.h"
+#include "slang-ir-util.h"
+
+namespace Slang
+{
+UInt findUnusedSpaceIndex(TargetProgram* targetProgram, IRModule* module)
+{
+ HashSet<int> usedSpaces;
+ auto processVarLayout = [&](IRVarLayout* varLayout)
+ {
+ UInt spaceOffset = 0;
+ if (auto spaceAttr = varLayout->findOffsetAttr(LayoutResourceKind::RegisterSpace))
+ {
+ spaceOffset = spaceAttr->getOffset();
+ }
+ for (auto sizeAttr : varLayout->getTypeLayout()->getSizeAttrs())
+ {
+ auto kind = sizeAttr->getResourceKind();
+ if (!ShaderBindingRange::isUsageTracked(kind))
+ continue;
+
+ if (auto offsetAttr = varLayout->findOffsetAttr(kind))
+ {
+ // Get the binding information from this attribute and insert it into the list
+ auto spaceIndex = spaceOffset + offsetAttr->getSpace();
+ usedSpaces.add((int)spaceIndex);
+ }
+ }
+ };
+
+ for (auto inst : module->getGlobalInsts())
+ {
+ if (as<IRGlobalParam>(inst))
+ {
+ auto varLayout = findVarLayout(inst);
+ if (!varLayout)
+ continue;
+ processVarLayout(varLayout);
+
+ auto paramGroupTypeLayout = as<IRParameterGroupTypeLayout>(varLayout->getTypeLayout());
+ if (!paramGroupTypeLayout)
+ continue;
+ auto containerVarLayout = paramGroupTypeLayout->getContainerVarLayout();
+ if (!containerVarLayout)
+ continue;
+ processVarLayout(containerVarLayout);
+ }
+ }
+
+ // Find next unused space index.
+ int index = targetProgram->getOptionSet().getIntOption(CompilerOptionName::BindlessSpaceIndex);
+ while (usedSpaces.contains(index))
+ {
+ index++;
+ }
+ return index;
+}
+
+IRVarLayout* createResourceHeapVarLayoutWithSpace(
+ IRBuilder& builder,
+ IRInst* param,
+ UInt spaceIndex)
+{
+ SLANG_UNUSED(param);
+ IRTypeLayout::Builder typeLayoutBuilder(&builder);
+ typeLayoutBuilder.addResourceUsage(
+ LayoutResourceKind::DescriptorTableSlot,
+ LayoutSize::infinite());
+ auto typeLayout = typeLayoutBuilder.build();
+ IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout);
+ varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::RegisterSpace)->offset = spaceIndex;
+ varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::DescriptorTableSlot)->offset = 0;
+ return varLayoutBuilder.build();
+}
+
+void lowerDynamicResourceHeap(TargetProgram* targetProgram, IRModule* module, DiagnosticSink* sink)
+{
+ SLANG_UNUSED(sink);
+ auto unusedSpaceIndex = findUnusedSpaceIndex(targetProgram, module);
+ List<IRInst*> workList;
+ for (auto globalInst : module->getGlobalInsts())
+ {
+ if (globalInst->getOp() == kIROp_GetDynamicResourceHeap)
+ {
+ workList.add(globalInst);
+ }
+ }
+ for (auto inst : workList)
+ {
+ auto arrayType = as<IRArrayTypeBase>(inst->getDataType());
+ IRBuilder builder(inst);
+ builder.setInsertBefore(inst);
+
+ auto param = builder.createGlobalParam(arrayType);
+ auto varLayout = createResourceHeapVarLayoutWithSpace(builder, param, unusedSpaceIndex);
+ builder.addLayoutDecoration(param, varLayout);
+ builder.addNameHintDecoration(param, toSlice("__slang_resource_heap"));
+ inst->replaceUsesWith(param);
+ }
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-ir-lower-dynamic-resource-heap.h b/source/slang/slang-ir-lower-dynamic-resource-heap.h
new file mode 100644
index 000000000..b4d0b320a
--- /dev/null
+++ b/source/slang/slang-ir-lower-dynamic-resource-heap.h
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace Slang
+{
+
+struct IRModule;
+class TargetProgram;
+class DiagnosticSink;
+
+/// Replace `GetDynamicResourceHeap` insts with an actual array of resources.
+void lowerDynamicResourceHeap(TargetProgram* targetProgram, IRModule* module, DiagnosticSink* sink);
+
+} // namespace Slang
diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp
index 4c01e5640..737209207 100644
--- a/source/slang/slang-ir-spirv-legalize.cpp
+++ b/source/slang/slang-ir-spirv-legalize.cpp
@@ -8,6 +8,7 @@
#include "slang-ir-composite-reg-to-mem.h"
#include "slang-ir-dce.h"
#include "slang-ir-dominators.h"
+#include "slang-ir-float-non-uniform-resource-index.h"
#include "slang-ir-glsl-legalize.h"
#include "slang-ir-insts.h"
#include "slang-ir-layout.h"
@@ -1098,130 +1099,6 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
addUsersToWorkList(newStore);
}
- void processNonUniformResourceIndex(IRInst* nonUniformResourceIndexInst)
- {
- // implement the translation to spirv by walking up the use-def chain
- // from nonUniformResource inst of an index to an array of buffer or
- // texture def all the way to the leaf operations. To be precise:
- // - go through GEP and see if it calls an intrinsic function,
- // then decorate the address itself (GetElementPtr)
- // - go through GEP to identify the pointer access and the Loads that it
- // accesses (GetElementPtr -> Load), then decorate the load instruction.
- // - go through IntCasts to deal with u32 -> i32 / vice-versa (IntCast)
- List<IRInst*> resWorkList;
-
- // Handle cases when `nonUniformResourceIndexInst` inst is wrapped around
- // an index in a nested fashion, i.e. nonUniform(nonUniform(index)) by
- // only adding the inner-most inst in the worklist, and work our way out.
- auto insti = nonUniformResourceIndexInst;
- while (insti->getOp() == kIROp_NonUniformResourceIndex)
- {
- if (resWorkList.getCount() != 0)
- resWorkList.removeLast();
- resWorkList.add(insti);
- insti = insti->getOperand(0);
- }
-
- // For all the users of a `nonUniformResourceIndexInst`, make them directly
- // use the underlying base inst that is wrapped by `nonUniformResourceIndex`
- // and finally wrap them with a `nonUniformResourceIndex`, and add back to the
- // worklist, and keep bubbling them up until it can.
- for (Index i = 0; i < resWorkList.getCount(); i++)
- {
- auto inst = resWorkList[i];
- traverseUses(
- inst,
- [&](IRUse* use)
- {
- auto user = use->getUser();
- IRBuilder builder(user);
- builder.setInsertBefore(user);
-
- IRInst* newUser = nullptr;
- switch (user->getOp())
- {
- case kIROp_IntCast:
- // Replace intCast(nonUniformRes(x)), into nonUniformRes(intCast(x))
- newUser = builder.emitCast(user->getFullType(), inst->getOperand(0));
- break;
- case kIROp_GetElementPtr:
- // Ignore when `NonUniformResourceIndex` is not on the index
- if (user->getOperand(1) == inst)
- {
- // Replace gep(pArray, nonUniformRes(x)), into
- // nonUniformRes(gep(pArray, x))
- newUser = builder.emitElementAddress(
- user->getFullType(),
- user->getOperand(0),
- inst->getOperand(0));
- }
- break;
- case kIROp_NonUniformResourceIndex:
- // Replace nonUniformRes(nonUniformRes(x)), into nonUniformRes(x)
- newUser = inst->getOperand(0);
- break;
- case kIROp_Load:
- // Replace load(nonUniformRes(x)), into nonUniformRes(load(x))
- newUser = builder.emitLoad(user->getFullType(), inst->getOperand(0));
- break;
- default:
- // Ignore for all other unknown insts.
- break;
- };
-
- // Early exit when we could not process the `NonUniformResourceIndex` inst.
- if (!newUser)
- return;
-
- auto nonuniformUser = builder.emitNonUniformResourceIndexInst(newUser);
- user->replaceUsesWith(nonuniformUser);
-
- // Update the worklist with the newly added `NonUniformResourceIndex` inst,
- // based on the base inst it was constructed around, in case we need to further
- // bubble up the `NonUniformResourceIndex` inst.
- switch (user->getOp())
- {
- case kIROp_IntCast:
- case kIROp_GetElementPtr:
- case kIROp_Load:
- case kIROp_NonUniformResourceIndex:
- resWorkList.add(nonuniformUser);
- break;
- };
-
- // Clean up the base inst from the IR module, to avoid duplicate decorations.
- user->removeAndDeallocate();
- });
- }
-
- // Once all the `NonUniformResourceIndex` insts are visited, and the inst type is bubbled up
- // to the parent, a decoration is added to the operands of the insts.
- for (int i = 0; i < resWorkList.getCount(); ++i)
- {
- // It is only required to decorate the base inst, if the `NonUniformResourceIndex` inst
- // around it has any active uses.
- auto inst = resWorkList[i];
- if (!inst->hasUses())
- {
- inst->removeAndDeallocate();
- continue;
- }
- // For each of the `NonUniformResourceIndex` inst that remain, decorate the base inst
- // with a [NonUniformResource] decoration, which is the operand0 of the inst, only
- // when the type is a resource type, or a pointer to a resource type, or a pointer
- // in the Physical Storage buffer address space.
- auto operand = inst->getOperand(0);
- auto type = operand->getDataType();
- if (isResourceType(type) || isPointerToResourceType(type))
- {
- IRBuilder builder(operand);
- builder.addSPIRVNonUniformResourceDecoration(operand);
- }
- inst->replaceUsesWith(operand);
- inst->removeAndDeallocate();
- }
- }
-
void processImageSubscript(IRImageSubscript* subscript)
{
if (auto ptrType = as<IRPtrTypeBase>(subscript->getDataType()))
@@ -1765,7 +1642,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
processRWStructuredBufferStore(inst);
break;
case kIROp_NonUniformResourceIndex:
- processNonUniformResourceIndex(inst);
+ processNonUniformResourceIndex(inst, NonUniformResourceIndexFloatMode::SPIRV);
break;
case kIROp_loop:
processLoop(as<IRLoop>(inst));
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index daeaca67b..e15ec6f07 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -8226,6 +8226,10 @@ bool IRInst::mightHaveSideEffects(SideEffectAnalysisOptions options)
case kIROp_CastPtrToInt:
case kIROp_CastIntToPtr:
case kIROp_PtrCast:
+ case kIROp_CastUInt2ToDescriptorHandle:
+ case kIROp_CastDescriptorHandleToUInt2:
+ case kIROp_CastDescriptorHandleToResource:
+ case kIROp_GetDynamicResourceHeap:
case kIROp_CastDynamicResource:
case kIROp_AllocObj:
case kIROp_BitfieldExtract:
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index b70c4e054..b29b3b815 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -1654,6 +1654,11 @@ struct IRRateQualifiedType : IRType
IR_LEAF_ISA(RateQualifiedType)
};
+struct IRDescriptorHandleType : IRType
+{
+ IRType* getResourceType() { return (IRType*)getOperand(0); }
+ IR_LEAF_ISA(DescriptorHandleType)
+};
// Unlike the AST-level type system where `TypeType` tracks the
// underlying type, the "type of types" in the IR is a simple
diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp
index cd269b185..e4f2b188d 100644
--- a/source/slang/slang-lookup.cpp
+++ b/source/slang/slang-lookup.cpp
@@ -616,7 +616,8 @@ static void _lookUpMembersInSuperTypeImpl(
request,
ioResult,
&derefBreacrumb);
- return;
+ if (ioResult.isValid())
+ return;
}
}
diff --git a/source/slang/slang-mangle.cpp b/source/slang/slang-mangle.cpp
index d955b7bd9..dedbb2d48 100644
--- a/source/slang/slang-mangle.cpp
+++ b/source/slang/slang-mangle.cpp
@@ -416,6 +416,19 @@ void emitQualifiedName(ManglingContext* context, DeclRef<Decl> declRef, bool inc
return;
}
+ if (auto genTypeParamDecl = as<GenericTypeParamDeclBase>(declRef.getDecl()))
+ {
+ emit(context, "GP");
+ emit(context, genTypeParamDecl->parameterIndex);
+ return;
+ }
+ if (auto genValParamDecl = as<GenericValueParamDecl>(declRef.getDecl()))
+ {
+ emit(context, "GP");
+ emit(context, genValParamDecl->parameterIndex);
+ return;
+ }
+
auto parentDeclRef = declRef.getParent();
if (as<FileDecl>(parentDeclRef))
parentDeclRef = parentDeclRef.getParent();
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 681999168..a0e8515eb 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -527,9 +527,7 @@ void initCommandOptions(CommandOptions& options)
{OptionKind::EmitReflectionJSON,
"-reflection-json",
"reflection-json <path>",
- "Emit reflection data in JSON format to a file."},
- };
-
+ "Emit reflection data in JSON format to a file."}};
_addOptions(makeConstArrayView(generalOpts), options);
@@ -672,7 +670,10 @@ void initCommandOptions(CommandOptions& options)
"-incomplete-library",
nullptr,
"Allow generating code from incomplete libraries with unresolved external functions"},
- };
+ {OptionKind::BindlessSpaceIndex,
+ "-bindless-space-index",
+ "-bindless-space-index <index>",
+ "Specify the space index for the system defined global bindless resource array."}};
_addOptions(makeConstArrayView(targetOpts), options);
@@ -2475,6 +2476,10 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
// value in only allowing a single `-profile` option per target while still allowing
// zero or more `-capability` options.
+ // Don't treat zero args as an error.
+ if (!m_reader.hasArg())
+ break;
+
CommandLineArg operand;
SLANG_RETURN_ON_FAIL(m_reader.expectArg(operand));
@@ -2899,6 +2904,13 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
linkage->m_optionSet.add(OptionKind::DisableShortCircuit, true);
break;
}
+ case OptionKind::BindlessSpaceIndex:
+ {
+ Int index = 0;
+ SLANG_RETURN_ON_FAIL(_expectInt(arg, index));
+ linkage->m_optionSet.add(OptionKind::BindlessSpaceIndex, (int)index);
+ break;
+ }
default:
{
// Hmmm, we looked up and produced a valid enum, but it wasn't handled in the
diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp
index 98a324d96..d7b303535 100644
--- a/source/slang/slang-type-layout.cpp
+++ b/source/slang/slang-type-layout.cpp
@@ -2523,7 +2523,7 @@ SourceLanguage getIntermediateSourceLanguageForTarget(TargetProgram* targetProgr
bool areResourceTypesBindlessOnTarget(TargetRequest* targetReq)
{
- return isCPUTarget(targetReq) || isCUDATarget(targetReq);
+ return isCPUTarget(targetReq) || isCUDATarget(targetReq) || isMetalTarget(targetReq);
}
static bool isD3D11Target(TargetRequest*)
@@ -4802,6 +4802,15 @@ static TypeLayoutResult _createTypeLayout(TypeLayoutContext& context, Type* type
type,
rules);
}
+ else if (auto resPtrType = as<DescriptorHandleType>(type))
+ {
+ if (areResourceTypesBindlessOnTarget(context.targetReq))
+ return _createTypeLayout(context, resPtrType->getElementType());
+ auto uint2Type = context.astBuilder->getVectorType(
+ context.astBuilder->getUIntType(),
+ context.astBuilder->getIntVal(context.astBuilder->getIntType(), 2));
+ return _createTypeLayout(context, uint2Type);
+ }
else if (auto optionalType = as<OptionalType>(type))
{
// OptionalType should be laid out the same way as Tuple<T, bool>.