From efa8d1ab40c24a15678dd4143c9cc7b7b64e04d8 Mon Sep 17 00:00:00 2001 From: dubiousconst282 <87553666+dubiousconst282@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:20:06 -0300 Subject: Add generic descriptor indexing intrinsic (#4389) * Add ResourceArray intrinsic type * Move aliased parameter generation to GLSL legalization * Add DynamicResourceEntry type for proxying layout of GenericResourceArray * Reimplement as DynamicResource * Add reflection test * Don't reuse alias cache between different parameters * Add dynamic cast extensions for buffer types * Minor format fix * Fix VarDecl diagnostics after finding non-appliable initializer candidates --------- Co-authored-by: Yong He --- source/slang/hlsl.meta.slang | 66 ++++++++++++++++++ source/slang/slang-ast-type.h | 4 ++ source/slang/slang-check-decl.cpp | 26 +++---- source/slang/slang-emit.cpp | 8 +++ source/slang/slang-ir-glsl-legalize.cpp | 94 ++++++++++++++++++++++++++ source/slang/slang-ir-glsl-legalize.h | 1 + source/slang/slang-ir-inst-defs.h | 5 ++ source/slang/slang-ir-specialize-resources.cpp | 2 + source/slang/slang-ir.cpp | 2 + source/slang/slang-ir.h | 1 + source/slang/slang-reflection-api.cpp | 4 ++ source/slang/slang-type-layout.cpp | 7 ++ 12 files changed, 208 insertions(+), 12 deletions(-) (limited to 'source') diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 9760f974a..1a112e1a9 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -20287,6 +20287,72 @@ struct ConstBufferPointer } } +// +// HLSL-like dynamic resources +// https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_DynamicResources.html +// +// For Khronos targets, `__DynamicResource` can be used to declare "untyped" global bindings as +// usual (e.g. unsized arrays for descriptor indexing), which will then be materialized into +// new aliased bindings for each distinct cast type. +// + +__magic_type(DynamicResourceType) +__intrinsic_type($(kIROp_DynamicResourceType)) +struct __DynamicResource +{ + __intrinsic_op($(kIROp_CastDynamicResource)) + T as>(); +} +interface __IDynamicResourceCastable +{ +} + +enum __DynamicResourceKind +{ + General = 0, // CBV_SRV_UAV + Sampler = 1 +} + +__generic +extension __TextureImpl : __IDynamicResourceCastable<__DynamicResourceKind.General> +{ + __intrinsic_op($(kIROp_CastDynamicResource)) + __implicit_conversion($(kConversionCost_GenericParamUpcast)) + __init(__DynamicResource res); +} + +${{{{ +const char* kDynamicResourceCastableTypes[] = { + "StructuredBuffer", "RWStructuredBuffer", + "AppendStructuredBuffer", "ConsumeStructuredBuffer", "RasterizerOrderedStructuredBuffer", + "ByteAddressBuffer", "RWByteAddressBuffer", "RasterizerOrderedByteAddressBuffer", + + "SamplerState", "SamplerComparisonState", + + "ConstantBuffer", "TextureBuffer", +}; + +for (auto typeName : kDynamicResourceCastableTypes) { + auto kind = strstr(typeName, "Sampler") ? "Sampler" : "General"; + + if (strstr(typeName, "StructuredBuffer")) + sb << "__generic\n"; + else if (strstr(typeName, "Buffer")) + sb << "__generic\n"; +}}}} + +extension $(typeName) : __IDynamicResourceCastable<__DynamicResourceKind.$(kind)> +{ + __intrinsic_op($(kIROp_CastDynamicResource)) + __implicit_conversion($(kConversionCost_GenericParamUpcast)) + __init(__DynamicResource res); +} + +${{{{ +} +}}}} + + __glsl_version(450) __glsl_extension(GL_ARB_shader_clock) [require(glsl_spirv, GL_ARB_shader_clock)] diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index b007ad854..0d1a89860 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -211,6 +211,10 @@ class PointerLikeType : public BuiltinGenericType SLANG_AST_CLASS(PointerLikeType) }; +class DynamicResourceType : public BuiltinType +{ + SLANG_AST_CLASS(DynamicResourceType) +}; // HLSL buffer-type resources diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 23c10ddc2..969c87981 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -2037,10 +2037,11 @@ namespace Slang if (overloadContext.bestCandidates[0].status != OverloadCandidate::Status::Applicable) { getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{}); - return; } - - getSink()->diagnose(varDecl, Diagnostics::ambiguousDefaultInitializerForType, type); + else + { + getSink()->diagnose(varDecl, Diagnostics::ambiguousDefaultInitializerForType, type); + } } else if(overloadContext.bestCandidate) { @@ -2053,16 +2054,17 @@ namespace Slang if (overloadContext.bestCandidate->status != OverloadCandidate::Status::Applicable) { getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{}); - return; } - - // If we had a single best candidate *and* it was applicable, - // then we use it to construct a new initial-value expression - // for the variable, that will be used for all downstream - // code generation. - // - varDecl->initExpr = CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate); - getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{ *overloadContext.bestCandidate, 0}); + else + { + // If we had a single best candidate *and* it was applicable, + // then we use it to construct a new initial-value expression + // for the variable, that will be used for all downstream + // code generation. + // + varDecl->initExpr = CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate); + getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{*overloadContext.bestCandidate, 0}); + } } } diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 417bbf200..9e21ccbfd 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -244,6 +244,7 @@ struct RequiredLoweringPassSet bool glslGlobalVar; bool glslSSBO; bool byteAddressBuffer; + bool dynamicResource; }; // Scan the IR module and determine which lowering/legalization passes are needed based @@ -347,6 +348,9 @@ void calcRequiredLoweringPassSet(RequiredLoweringPassSet& result, CodeGenContext case kIROp_HLSLByteAddressBufferType: result.byteAddressBuffer = true; break; + case kIROp_DynamicResourceType: + result.dynamicResource = true; + break; } if (!result.generics || !result.existentialTypeLayout) { @@ -1166,6 +1170,10 @@ Result linkAndOptimizeIR( if(isD3DTarget(targetRequest)) legalizeNonStructParameterToStructForHLSL(irModule); + // Create aliases for all dynamic resource parameters. + if(requiredLoweringPassSet.dynamicResource && isKhronosTarget(targetRequest)) + legalizeDynamicResourcesForGLSL(codeGenContext, irModule); + legalizeExtractFromTextureAccess(irModule); // Legalize `ImageSubscript` loads. diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index 6ff52688b..404111a85 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -3616,4 +3616,98 @@ void legalizeDispatchMeshPayloadForGLSL(IRModule* module) }); } +void legalizeDynamicResourcesForGLSL(CodeGenContext* context, IRModule* module) +{ + List toRemove; + + for (auto inst : module->getGlobalInsts()) + { + auto param = as(inst); + + if (!param) + continue; + + // We are only interested in parameters involving `DynamicResource`, or arrays of it. + auto arrayType = as(param->getDataType()); + auto type = arrayType ? arrayType->getElementType() : param->getDataType(); + + if (!as(type)) + continue; + + Dictionary aliasedParams; + IRBuilder builder(module); + + auto getAliasedParam = [&](IRType* type) + { + IRGlobalParam* newParam; + + if (!aliasedParams.tryGetValue(type, newParam)) + { + newParam = builder.createGlobalParam(type); + + for (auto decoration : param->getDecorations()) + cloneDecoration(decoration, newParam); + + aliasedParams[type] = newParam; + } + return newParam; + }; + + // Try to rewrite all uses leading to `CastDynamicResource`. + // Later, we will diagnose an error if the parameter still has uses. + traverseUsers(param, [&](IRInst* user) + { + if (user->getOp() == kIROp_CastDynamicResource && !arrayType) + { + builder.setInsertBefore(user); + + user->replaceUsesWith(getAliasedParam(user->getDataType())); + user->removeAndDeallocate(); + } + else if (user->getOp() == kIROp_GetElement && arrayType) + { + traverseUsers(user, [&](IRInst* elementUser) + { + if (elementUser->getOp() == kIROp_CastDynamicResource) + { + builder.setInsertBefore(elementUser); + + auto paramType = builder.getArrayTypeBase( + arrayType->getOp(), + elementUser->getDataType(), + arrayType->getElementCount()); + + auto newAccess = builder.emitElementExtract( + paramType->getElementType(), + getAliasedParam(paramType), + user->getOperand(1)); + + elementUser->replaceUsesWith(newAccess); + elementUser->removeAndDeallocate(); + } + }); + + if (!user->hasUses()) + { + user->removeAndDeallocate(); + } + } + }); + toRemove.add(param); + } + + // Remove unused parameters later to avoid invalidating iterator. + for (auto param : toRemove) + { + if (!param->hasUses()) + { + param->removeAndDeallocate(); + } + else + { + context->getSink()->diagnose(param->firstUse->getUser(), Diagnostics::ambiguousReference, param); + } + } +} + } // namespace Slang diff --git a/source/slang/slang-ir-glsl-legalize.h b/source/slang/slang-ir-glsl-legalize.h index 8ec164a96..c9e6e3f26 100644 --- a/source/slang/slang-ir-glsl-legalize.h +++ b/source/slang/slang-ir-glsl-legalize.h @@ -25,4 +25,5 @@ void legalizeConstantBufferLoadForGLSL(IRModule* module); void legalizeDispatchMeshPayloadForGLSL(IRModule* module); +void legalizeDynamicResourcesForGLSL(CodeGenContext* context, IRModule* module); } diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 2b5f35736..9899feba1 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -223,6 +223,9 @@ INST(Nop, nop, 0, 0) INST(RayQueryType, RayQuery, 1, HOISTABLE) INST(HitObjectType, HitObject, 0, HOISTABLE) +// Opaque type that can be dynamically cast to other resource types. +INST(DynamicResourceType, DynamicResource, 0, HOISTABLE) + // A user-defined structure declaration at the IR level. // Unlike in the AST where there is a distinction between // a `StructDecl` and a `DeclRefType` that refers to it, @@ -403,6 +406,8 @@ INST(GetElementPtr, getElementPtr, 2, 0) INST(GetOffsetPtr, getOffsetPtr, 2, 0) INST(GetAddr, getAddr, 1, 0) +INST(CastDynamicResource, castDynamicResource, 1, 0) + // Get an unowned NativeString from a String. INST(getNativeStr, getNativeStr, 1, 0) diff --git a/source/slang/slang-ir-specialize-resources.cpp b/source/slang/slang-ir-specialize-resources.cpp index 419477790..c4be278df 100644 --- a/source/slang/slang-ir-specialize-resources.cpp +++ b/source/slang/slang-ir-specialize-resources.cpp @@ -1246,6 +1246,8 @@ bool isIllegalGLSLParameterType(IRType* type) return true; if (as(type)) return true; + if (as(type)) + return true; return false; } diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index c6409a7e1..ba7376c46 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -8198,6 +8198,7 @@ namespace Slang case kIROp_CastPtrToInt: case kIROp_CastIntToPtr: case kIROp_PtrCast: + case kIROp_CastDynamicResource: case kIROp_AllocObj: case kIROp_PackAnyValue: case kIROp_UnpackAnyValue: @@ -8638,6 +8639,7 @@ namespace Slang case kIROp_CastPtrToBool: case kIROp_CastPtrToInt: case kIROp_PtrCast: + case kIROp_CastDynamicResource: case kIROp_BitAnd: case kIROp_BitNot: case kIROp_BitOr: diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index cf9dfa84c..9a773a891 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1562,6 +1562,7 @@ SIMPLE_IR_TYPE(TextureBufferType, UniformParameterGroupType) SIMPLE_IR_TYPE(GLSLInputParameterGroupType, VaryingParameterGroupType) SIMPLE_IR_TYPE(GLSLOutputParameterGroupType, VaryingParameterGroupType) SIMPLE_IR_TYPE(ParameterBlockType, UniformParameterGroupType) +SIMPLE_IR_TYPE(DynamicResourceType, Type) struct IRGLSLShaderStorageBufferType : IRBuiltinGenericType { diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp index 8dc889c97..88caf67be 100644 --- a/source/slang/slang-reflection-api.cpp +++ b/source/slang/slang-reflection-api.cpp @@ -383,6 +383,10 @@ SLANG_API SlangTypeKind spReflectionType_GetKind(SlangReflectionType* inType) { return SLANG_TYPE_KIND_POINTER; } + else if (const auto dynamicResourceType = as(type)) + { + return SLANG_TYPE_KIND_DYNAMIC_RESOURCE; + } // TODO: need a better way to handle this stuff... #define CASE(TYPE) \ else if(as(type)) do { \ diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index eca0d0ec3..f85fb2c5f 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -4389,6 +4389,13 @@ static TypeLayoutResult _createTypeLayout( return result; } + else if (as(type)) + { + return createSimpleTypeLayout( + SimpleLayoutInfo(LayoutResourceKind::DescriptorTableSlot, 1), + type, + rules); + } else if (auto declRefType = as(type)) { auto declRef = declRefType->getDeclRef(); -- cgit v1.2.3