diff options
| -rw-r--r-- | docs/user-guide/a2-02-metal-target-specific.md | 19 | ||||
| -rw-r--r-- | docs/user-guide/a2-03-wgsl-target-specific.md | 18 | ||||
| -rw-r--r-- | docs/user-guide/toc.html | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit-metal.cpp | 46 | ||||
| -rw-r--r-- | source/slang/slang-emit-metal.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-emit-wgsl.cpp | 33 | ||||
| -rw-r--r-- | source/slang/slang-emit-wgsl.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-explicit-global-context.cpp | 24 | ||||
| -rw-r--r-- | source/slang/slang-parameter-binding.cpp | 134 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.cpp | 2 | ||||
| -rw-r--r-- | tests/compute/generics-constrained.slang | 3 | ||||
| -rw-r--r-- | tests/spirv/specialization-constant.slang | 19 |
12 files changed, 227 insertions, 75 deletions
diff --git a/docs/user-guide/a2-02-metal-target-specific.md b/docs/user-guide/a2-02-metal-target-specific.md index 827f11200..e2602eb77 100644 --- a/docs/user-guide/a2-02-metal-target-specific.md +++ b/docs/user-guide/a2-02-metal-target-specific.md @@ -274,4 +274,21 @@ The HLSL `:register()` semantic is respected when emitting Metal code. Since metal does not differentiate a constant buffer, a shader resource (read-only) buffer and an unordered access buffer, Slang will map `register(tN)`, `register(uN)` and `register(bN)` to `[[buffer(N)]]` when such `register` semantic is declared on a buffer typed parameter. -`spaceN` specifiers inside `register` semantics are ignored.
\ No newline at end of file +`spaceN` specifiers inside `register` semantics are ignored. + +## Specialization Constants + +Specialization constants declared with the `[SpecializationConstant]` or `[vk::constant_id]` attribute will be translated into a `function_constant` when generating Metal source. +For example: + +```csharp +[vk::constant_id(7)] +const int a = 2; +``` + +Translates to: + +```metal +constant int fc_a_0 [[function_constant(7)]]; +constant int a_0 = is_function_constant_defined(fc_a_0) ? fc_a_0 : 2; +```
\ No newline at end of file diff --git a/docs/user-guide/a2-03-wgsl-target-specific.md b/docs/user-guide/a2-03-wgsl-target-specific.md index c802a682b..743928a5f 100644 --- a/docs/user-guide/a2-03-wgsl-target-specific.md +++ b/docs/user-guide/a2-03-wgsl-target-specific.md @@ -161,4 +161,20 @@ Since the WGSL matrix multiplication convention is the normal one, where inner p The `[vk::binding(index,set)]` attribute is respected when emitting WGSL code, and will translate to `@binding(index) @group(set)` in WGSL. -If the `[vk::binding()]` attribute is not specified by a `:register()` semantic is present, Slang will derive the binding from the `register` semantic the same way as the SPIRV and GLSL backends.
\ No newline at end of file +If the `[vk::binding()]` attribute is not specified by a `:register()` semantic is present, Slang will derive the binding from the `register` semantic the same way as the SPIRV and GLSL backends. + +## Specialization Constants + +Specialization constants declared with the `[SpecializationConstant]` or `[vk::constant_id]` attribute will be translated into a global `override` declaration when generating WGSL source. +For example: + +```csharp +[vk::constant_id(7)] +const int a = 2; +``` + +Translates to: + +```wgsl +@id(7) override a : i32 = 2; +```
\ No newline at end of file diff --git a/docs/user-guide/toc.html b/docs/user-guide/toc.html index 39cfd6164..283706adc 100644 --- a/docs/user-guide/toc.html +++ b/docs/user-guide/toc.html @@ -234,6 +234,7 @@ <li data-link="metal-target-specific#conservative-rasterization"><span>Conservative Rasterization</span></li> <li data-link="metal-target-specific#address-space-assignment"><span>Address Space Assignment</span></li> <li data-link="metal-target-specific#explicit-parameter-binding"><span>Explicit Parameter Binding</span></li> +<li data-link="metal-target-specific#specialization-constants"><span>Specialization Constants</span></li> </ul> </li> <li data-link="wgsl-target-specific"><span>WGSL specific functionalities</span> @@ -251,6 +252,7 @@ <li data-link="wgsl-target-specific#address-space-assignment"><span>Address Space Assignment</span></li> <li data-link="wgsl-target-specific#matrix-type-translation"><span>Matrix type translation</span></li> <li data-link="wgsl-target-specific#explicit-parameter-binding"><span>Explicit Parameter Binding</span></li> +<li data-link="wgsl-target-specific#specialization-constants"><span>Specialization Constants</span></li> </ul> </li> </ul> diff --git a/source/slang/slang-emit-metal.cpp b/source/slang/slang-emit-metal.cpp index 3548e9369..e6e2a3ec5 100644 --- a/source/slang/slang-emit-metal.cpp +++ b/source/slang/slang-emit-metal.cpp @@ -1331,9 +1331,53 @@ bool MetalSourceEmitter::_emitUserSemantic( return false; } +bool MetalSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) +{ + auto layout = getVarLayout(varDecl); + if (!layout) + return false; + if (auto specConstLayout = layout->findOffsetAttr(LayoutResourceKind::SpecializationConstant)) + { + // Emit specialization constant. + auto name = getName(varDecl); + auto prefixName = "fc_" + name; + auto defaultVal = varDecl->findDecoration<IRDefaultValueDecoration>(); + + m_writer->emit("constant "); + emitType(varType, prefixName); + m_writer->emit(" "); + m_writer->emit("[[function_constant("); + m_writer->emit(specConstLayout->getOffset()); + m_writer->emit(")]];\n"); + + m_writer->emit("constant "); + emitType(varType, name); + m_writer->emit(" = "); + if (defaultVal) + { + m_writer->emit("is_function_constant_defined("); + m_writer->emit(prefixName); + m_writer->emit(") ? "); + m_writer->emit(prefixName); + m_writer->emit(" : "); + emitVal(defaultVal->getOperand(0), getInfo(EmitOp::General)); + } + else + { + m_writer->emit(prefixName); + } + m_writer->emit(";\n"); + return true; + } + return false; +} + void MetalSourceEmitter::emitSemanticsImpl(IRInst* inst, bool allowOffsets) { SLANG_UNUSED(allowOffsets); + + auto varLayout = findVarLayout(inst); + if (inst->getOp() == kIROp_StructKey) { // Only emit [[attribute(n)]] on struct keys. @@ -1341,7 +1385,6 @@ void MetalSourceEmitter::emitSemanticsImpl(IRInst* inst, bool allowOffsets) if (maybeEmitSystemSemantic(inst)) return; - auto varLayout = findVarLayout(inst); bool hasSemantic = false; if (varLayout) @@ -1378,6 +1421,7 @@ void MetalSourceEmitter::emitSemanticsImpl(IRInst* inst, bool allowOffsets) semanticDecor->getSemanticIndex()); } } + return; } } diff --git a/source/slang/slang-emit-metal.h b/source/slang/slang-emit-metal.h index 7cd01c646..17562b1c4 100644 --- a/source/slang/slang-emit-metal.h +++ b/source/slang/slang-emit-metal.h @@ -40,6 +40,7 @@ protected: virtual void emitRateQualifiersAndAddressSpaceImpl(IRRate* rate, AddressSpace addressSpace) SLANG_OVERRIDE; + virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; virtual void emitSemanticsImpl(IRInst* inst, bool allowOffsets) SLANG_OVERRIDE; virtual void emitSimpleFuncParamImpl(IRParam* param) SLANG_OVERRIDE; virtual void emitPostDeclarationAttributesForType(IRInst* type) SLANG_OVERRIDE; diff --git a/source/slang/slang-emit-wgsl.cpp b/source/slang/slang-emit-wgsl.cpp index cba9c1b54..8d4b33541 100644 --- a/source/slang/slang-emit-wgsl.cpp +++ b/source/slang/slang-emit-wgsl.cpp @@ -643,6 +643,21 @@ void WGSLSourceEmitter::emitSimpleTypeImpl(IRType* type) } } +void WGSLSourceEmitter::emitGlobalParamDefaultVal(IRGlobalParam* varDecl) +{ + auto layout = getVarLayout(varDecl); + if (!layout) + return; + if (layout->findOffsetAttr(LayoutResourceKind::SpecializationConstant)) + { + if (auto defaultValDecor = varDecl->findDecoration<IRDefaultValueDecoration>()) + { + m_writer->emit(" = "); + emitInstExpr(defaultValDecor->getOperand(0), EmitOpInfo()); + } + } +} + void WGSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) { @@ -670,6 +685,14 @@ void WGSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) return; } + else if (kind == LayoutResourceKind::SpecializationConstant) + { + m_writer->emit("@id("); + m_writer->emit(attr->getOffset()); + m_writer->emit(") "); + + return; + } } } @@ -708,7 +731,15 @@ void WGSLSourceEmitter::emitVarKeywordImpl(IRType* type, IRInst* varDecl) case kIROp_GlobalParam: case kIROp_GlobalVar: case kIROp_Var: - m_writer->emit("var"); + { + auto layout = getVarLayout(varDecl); + if (layout && layout->findOffsetAttr(LayoutResourceKind::SpecializationConstant)) + { + m_writer->emit("override"); + break; + } + m_writer->emit("var"); + } break; default: if (isStaticConst(varDecl)) diff --git a/source/slang/slang-emit-wgsl.h b/source/slang/slang-emit-wgsl.h index 0cbedf4eb..28311aa27 100644 --- a/source/slang/slang-emit-wgsl.h +++ b/source/slang/slang-emit-wgsl.h @@ -52,6 +52,7 @@ public: UnownedStringSlice intrinsicDefinition, IRInst* intrinsicInst, EmitOpInfo const& inOuterPrec) SLANG_OVERRIDE; + virtual void emitGlobalParamDefaultVal(IRGlobalParam* varDecl) SLANG_OVERRIDE; void emit(const AddressSpace addressSpace); diff --git a/source/slang/slang-ir-explicit-global-context.cpp b/source/slang/slang-ir-explicit-global-context.cpp index d891263cd..0516d574c 100644 --- a/source/slang/slang-ir-explicit-global-context.cpp +++ b/source/slang/slang-ir-explicit-global-context.cpp @@ -3,6 +3,7 @@ #include "slang-ir-clone.h" #include "slang-ir-insts.h" +#include "slang-ir-util.h" namespace Slang { @@ -39,7 +40,8 @@ struct IntroduceExplicitGlobalContextPass class ExplicitContextPolicy { public: - ExplicitContextPolicy(CodeGenTarget target) + ExplicitContextPolicy(CodeGenTarget inTarget) + : target(inTarget) { switch (target) { @@ -74,7 +76,7 @@ struct IntroduceExplicitGlobalContextPass return (UInt)hoistableGlobalObjectKind & (UInt)hoistable; } - bool canHoistGlobalVar(IRGlobalVar* inst) + bool canHoistGlobalVar(IRInst* inst) { if (!((UInt)hoistGlobalVarOptions & (UInt)HoistGlobalVarOptions::SharedGlobal) && as<IRGroupSharedRate>(inst->getRate())) @@ -99,6 +101,19 @@ struct IntroduceExplicitGlobalContextPass } } + // Do not move specialization constants to context. + switch (target) + { + case CodeGenTarget::Metal: + case CodeGenTarget::MetalLib: + case CodeGenTarget::MetalLibAssembly: + { + auto varLayout = findVarLayout(inst); + if (varLayout && + varLayout->findOffsetAttr(LayoutResourceKind::SpecializationConstant)) + return false; + } + } return true; } @@ -111,6 +126,7 @@ struct IntroduceExplicitGlobalContextPass GlobalObjectKind hoistableGlobalObjectKind = GlobalObjectKind::All; bool requiresFuncTypeCorrectionPass = false; AddressSpace addressSpaceOfLocals = AddressSpace::ThreadLocal; + CodeGenTarget target; }; IntroduceExplicitGlobalContextPass(IRModule* module, CodeGenTarget target) @@ -134,7 +150,7 @@ struct IntroduceExplicitGlobalContextPass bool canHoistType(GlobalObjectKind hoistable) { return m_options.canHoistType(hoistable); } - bool canHoistGlobalVar(IRGlobalVar* inst) { return m_options.canHoistGlobalVar(inst); } + bool canHoistGlobalVar(IRInst* inst) { return m_options.canHoistGlobalVar(inst); } void processModule() { @@ -183,6 +199,8 @@ struct IntroduceExplicitGlobalContextPass // auto globalParam = cast<IRGlobalParam>(inst); + if (!canHoistGlobalVar(globalParam)) + continue; // One detail we need to be careful about is that as a result // of legalizing the varying parameters of compute kernels to diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index f9c4f0c94..1303a59f1 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -1081,25 +1081,9 @@ static void addExplicitParameterBindings_GLSL( RefPtr<ParameterInfo> parameterInfo, RefPtr<VarLayout> varLayout) { - // We only want to apply GLSL-style layout modifers - // when compiling for a Khronos-related target. - // - // TODO: This should have some finer granularity - // so that we are able to distinguish between - // Vulkan and OpenGL as targets. - // - if (!isKhronosTarget(context->getTargetRequest()) && !isWGPUTarget(context->getTargetRequest())) - return; - auto typeLayout = varLayout->typeLayout; auto varDecl = varLayout->varDecl; - // The catch in GLSL is that the expected resource type - // is implied by the parameter declaration itself, and - // the `layout` modifier is only allowed to adjust - // the index/offset/etc. - // - enum { kResInfo = 0, @@ -1120,56 +1104,12 @@ static void addExplicitParameterBindings_GLSL( }; ResAndSemanticInfo info[kMaxResCount] = {}; - if (auto foundInputAttachmentIndex = - typeLayout->FindResourceInfo(LayoutResourceKind::InputAttachmentIndex)) - { - foundResInfo = foundInputAttachmentIndex; - // Try to find `input_attachment_index` - if (auto glslAttachmentIndexAttr = - varDecl.getDecl()->findModifier<GLSLInputAttachmentIndexLayoutAttribute>()) - { - info[kSubpassResInfo].resInfo = foundResInfo; - // Subpass fills semantic info of a descriptor and subpass - info[kSubpassResInfo].semanticInfo.index = (UInt)glslAttachmentIndexAttr->location; - info[kSubpassResInfo].semanticInfo.space = 0; - } - } + // First, we want to apply offsets of specialization constants for platforms that supports them. + if (!isKhronosTarget(context->getTargetRequest()) && + !isWGPUTarget(context->getTargetRequest()) && !isMetalTarget(context->getTargetRequest())) + return; - if (auto foundDescriptorTableSlot = - typeLayout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) - { - foundResInfo = foundDescriptorTableSlot; - // Try to find `binding` and `set` - if (auto glslBindingAttr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>()) - { - info[kResInfo].resInfo = foundResInfo; - info[kResInfo].semanticInfo.index = glslBindingAttr->binding; - info[kResInfo].semanticInfo.space = glslBindingAttr->set; - } - } - else if ( - auto foundSubElementRegisterSpace = - typeLayout->FindResourceInfo(LayoutResourceKind::SubElementRegisterSpace)) - { - foundResInfo = foundSubElementRegisterSpace; - // Try to find `set` - if (auto attr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>()) - { - info[kResInfo].resInfo = foundResInfo; - if (attr->binding != 0) - { - getSink(context)->diagnose( - attr, - Diagnostics::wholeSpaceParameterRequiresZeroBinding, - varDecl.getName(), - attr->binding); - } - info[kResInfo].semanticInfo.index = attr->set; - info[kResInfo].semanticInfo.space = 0; - } - } - else if ( - auto foundSpecializationConstant = + if (auto foundSpecializationConstant = typeLayout->FindResourceInfo(LayoutResourceKind::SpecializationConstant)) { info[kResInfo].resInfo = foundSpecializationConstant; @@ -1180,6 +1120,70 @@ static void addExplicitParameterBindings_GLSL( return; } + // For remaining cases, we only want to apply GLSL-style layout modifers + // when compiling for Khronos and WGSL targets. + // + // TODO: This should have some finer granularity + // so that we are able to distinguish between + // Vulkan and OpenGL as targets. + // + if (isKhronosTarget(context->getTargetRequest()) || isWGPUTarget(context->getTargetRequest())) + { + // The catch in GLSL is that the expected resource type + // is implied by the parameter declaration itself, and + // the `layout` modifier is only allowed to adjust + // the index/offset/etc. + // + + if (auto foundInputAttachmentIndex = + typeLayout->FindResourceInfo(LayoutResourceKind::InputAttachmentIndex)) + { + foundResInfo = foundInputAttachmentIndex; + // Try to find `input_attachment_index` + if (auto glslAttachmentIndexAttr = + varDecl.getDecl()->findModifier<GLSLInputAttachmentIndexLayoutAttribute>()) + { + info[kSubpassResInfo].resInfo = foundResInfo; + // Subpass fills semantic info of a descriptor and subpass + info[kSubpassResInfo].semanticInfo.index = (UInt)glslAttachmentIndexAttr->location; + info[kSubpassResInfo].semanticInfo.space = 0; + } + } + + if (auto foundDescriptorTableSlot = + typeLayout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) + { + foundResInfo = foundDescriptorTableSlot; + // Try to find `binding` and `set` + if (auto glslBindingAttr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>()) + { + info[kResInfo].resInfo = foundResInfo; + info[kResInfo].semanticInfo.index = glslBindingAttr->binding; + info[kResInfo].semanticInfo.space = glslBindingAttr->set; + } + } + else if ( + auto foundSubElementRegisterSpace = + typeLayout->FindResourceInfo(LayoutResourceKind::SubElementRegisterSpace)) + { + foundResInfo = foundSubElementRegisterSpace; + // Try to find `set` + if (auto attr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>()) + { + info[kResInfo].resInfo = foundResInfo; + if (attr->binding != 0) + { + getSink(context)->diagnose( + attr, + Diagnostics::wholeSpaceParameterRequiresZeroBinding, + varDecl.getName(), + attr->binding); + } + info[kResInfo].semanticInfo.index = attr->set; + info[kResInfo].semanticInfo.space = 0; + } + } + } auto varDeclBase = as<VarDeclBase>(varDecl); bool hasABinding = false; diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index da4cd458b..5e63a7cfd 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -2013,7 +2013,7 @@ LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getVaryingOutputRules() LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getSpecializationConstantRules() { - return nullptr; + return &kGLSLSpecializationConstantLayoutRulesImpl_; } LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getShaderStorageBufferRules(CompilerOptionSet&) diff --git a/tests/compute/generics-constrained.slang b/tests/compute/generics-constrained.slang index 04a9c59b7..ccb8413f8 100644 --- a/tests/compute/generics-constrained.slang +++ b/tests/compute/generics-constrained.slang @@ -28,8 +28,7 @@ float testHelp(T helper) } //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer -[vk::binding(0, 0)] -RWStructuredBuffer<float> outputBuffer : register(u0); +RWStructuredBuffer<float> outputBuffer; [numthreads(4, 1, 1)] diff --git a/tests/spirv/specialization-constant.slang b/tests/spirv/specialization-constant.slang index dbf315926..44a7e4432 100644 --- a/tests/spirv/specialization-constant.slang +++ b/tests/spirv/specialization-constant.slang @@ -1,8 +1,27 @@ +//TEST:SIMPLE(filecheck=METAL): -target metal +//TEST:SIMPLE(filecheck=WGSL): -target wgsl -entry computeMain -stage compute //TEST:SIMPLE(filecheck=GLSL): -target glsl -allow-glsl //TEST:SIMPLE(filecheck=GLSL): -target glsl //TEST:SIMPLE(filecheck=CHECK): -target spirv -allow-glsl //TEST:SIMPLE(filecheck=CHECK): -target spirv +// METAL-DAG: constant bool fc_constValue2{{.*}} {{\[\[}}function_constant(1){{\]\]}}; +// METAL-DAG: constant bool constValue2{{.*}} = is_function_constant_defined(fc_constValue2{{.*}}) ? fc_constValue2{{.*}} : true; + +// METAL-DAG: constant float fc_constValue1{{.*}} {{\[\[}}function_constant(7){{\]\]}}; + +// METAL-DAG: constant int fc_constValue0{{.*}} {{\[\[}}function_constant(0){{\]\]}}; + +// METAL-DAG: constant int fc_constValue3{{.*}} {{\[\[}}function_constant(9){{\]\]}}; + +// WGSL-DAG: @id(0) override constValue0{{.*}} = {{.*}} + +// WGSL-DAG: @id(7) override constValue1{{.*}} = {{.*}} + +// WGSL-DAG: @id(1) override constValue2{{.*}} = {{.*}} + +// WGSL-DAG: @id(9) override constValue3{{.*}} = {{.*}} + // CHECK-DAG: OpDecorate %[[C0:[0-9A-Za-z_]+]] SpecId 0 // CHECK-DAG: %[[C0]] = OpSpecConstant %int 1 |
