summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/user-guide/a2-02-metal-target-specific.md19
-rw-r--r--docs/user-guide/a2-03-wgsl-target-specific.md18
-rw-r--r--docs/user-guide/toc.html2
-rw-r--r--source/slang/slang-emit-metal.cpp46
-rw-r--r--source/slang/slang-emit-metal.h1
-rw-r--r--source/slang/slang-emit-wgsl.cpp33
-rw-r--r--source/slang/slang-emit-wgsl.h1
-rw-r--r--source/slang/slang-ir-explicit-global-context.cpp24
-rw-r--r--source/slang/slang-parameter-binding.cpp134
-rw-r--r--source/slang/slang-type-layout.cpp2
-rw-r--r--tests/compute/generics-constrained.slang3
-rw-r--r--tests/spirv/specialization-constant.slang19
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