diff options
| author | Yong He <yonghe@outlook.com> | 2025-01-10 10:57:04 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-10 10:57:04 -0800 |
| commit | 5290c580632cfb56847b863a32dc020a21d1c93e (patch) | |
| tree | 4c543f28d13f62a1dc3293b76151dda7585743ab /source/slang | |
| parent | 4cfae806a6f9c0203ce44c4ce04df5ad66cdc8a2 (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')
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>. |
