From 7dabfa76ccfb396e9d2019e2b6e01259d1661dc5 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 5 Dec 2024 19:33:55 -0800 Subject: Implement explciit binding for metal and wgsl. (#5778) * Respect explicit bindings in wgsl emit. * Implement explciit binding generation for metal and wgsl. * Update toc. * Fix warnings in tests. * Fix tests. --------- Co-authored-by: Ellie Hermaszewska --- docs/user-guide/a2-02-metal-target-specific.md | 8 ++++++++ docs/user-guide/a2-03-wgsl-target-specific.md | 6 ++++++ docs/user-guide/toc.html | 2 ++ source/slang/slang-emit-wgsl.cpp | 2 +- source/slang/slang-parameter-binding.cpp | 19 ++++++++++++++++-- tests/bugs/gh-471.slang | 2 +- tests/bugs/gh-775.slang | 2 +- tests/bugs/static-method.slang | 2 +- tests/bugs/static-var.slang | 2 +- tests/bugs/texture2d-gather.hlsl | 5 +++-- tests/bugs/type-legalize-bug-1.slang | 2 +- tests/compute/break-stmt.slang | 2 +- tests/compute/continue-stmt.slang | 2 +- tests/compute/default-initializer.slang | 2 +- tests/compute/explicit-this-expr.slang | 2 +- tests/compute/generics-constrained.slang | 1 + tests/compute/global-init.slang | 2 +- tests/compute/implicit-generic-app.slang | 2 +- tests/compute/implicit-this-expr.slang | 2 +- tests/compute/init-list-defaults.slang | 2 +- tests/compute/inout.slang | 2 +- tests/compute/multiple-continue-sites.slang | 2 +- tests/compute/struct-default-init.slang | 2 +- tests/compute/switch-stmt.slang | 2 +- tests/compute/this-type.slang | 2 +- tests/compute/user-defined-initializer.slang | 2 +- .../properties/property-in-interface.slang | 2 +- tests/preprocessor/line-macro.slang | 2 +- tests/serialization/std-lib-serialize.slang | 2 +- tests/wgsl/explicit-binding.slang | 23 ++++++++++++++++++++++ 30 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 tests/wgsl/explicit-binding.slang diff --git a/docs/user-guide/a2-02-metal-target-specific.md b/docs/user-guide/a2-02-metal-target-specific.md index a69f466a9..827f11200 100644 --- a/docs/user-guide/a2-02-metal-target-specific.md +++ b/docs/user-guide/a2-02-metal-target-specific.md @@ -267,3 +267,11 @@ Metal requires explicit address space qualifiers. Slang automatically assigns ap | RW/Structured Buffers | `device` | | Group Shared | `threadgroup` | | Parameter Blocks | `constant` | + +## Explicit Parameter Binding + +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 diff --git a/docs/user-guide/a2-03-wgsl-target-specific.md b/docs/user-guide/a2-03-wgsl-target-specific.md index 75a2a6da3..c802a682b 100644 --- a/docs/user-guide/a2-03-wgsl-target-specific.md +++ b/docs/user-guide/a2-03-wgsl-target-specific.md @@ -156,3 +156,9 @@ Matrix type translation A m-row-by-n-column matrix in Slang, represented as float`m`x`n` or matrix, is translated to `mat[n]x[m]` in WGSL, i.e. a matrix with `n` columns and `m` rows. The rationale for this inversion of terminology is the same as [the rationale for SPIR-V](a2-01-spirv-target-specific.md#matrix-type-translation). Since the WGSL matrix multiplication convention is the normal one, where inner products of rows of the matrix on the left are taken with columns of the matrix on the right, the order of matrix products is also reversed in WGSL. This is relying on the fact that the transpose of a matrix product equals the product of the transposed matrix operands in reverse order. + +## Explicit Parameter Binding + +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 diff --git a/docs/user-guide/toc.html b/docs/user-guide/toc.html index 2cb6ce5e6..39cfd6164 100644 --- a/docs/user-guide/toc.html +++ b/docs/user-guide/toc.html @@ -233,6 +233,7 @@
  • Value Type Conversion
  • Conservative Rasterization
  • Address Space Assignment
  • +
  • Explicit Parameter Binding
  • WGSL specific functionalities @@ -249,6 +250,7 @@
  • Pointers
  • Address Space Assignment
  • Matrix type translation
  • +
  • Explicit Parameter Binding
  • diff --git a/source/slang/slang-emit-wgsl.cpp b/source/slang/slang-emit-wgsl.cpp index 13c14cf34..cba9c1b54 100644 --- a/source/slang/slang-emit-wgsl.cpp +++ b/source/slang/slang-emit-wgsl.cpp @@ -663,7 +663,7 @@ void WGSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) EmitVarChain chain = {}; chain.varLayout = layout; - auto space = getBindingSpaceForKinds(&chain, kind); + auto space = getBindingSpaceForKinds(&chain, LayoutResourceKindFlag::make(kind)); m_writer->emit("@group("); m_writer->emit(space); m_writer->emit(") "); diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index e45eb4652..f9c4f0c94 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -984,7 +984,8 @@ static void addExplicitParameterBindings_HLSL( // // For now we do the filtering on target in a very direct fashion: // - if (!isD3DTarget(context->getTargetRequest()) && !isMetalTarget(context->getTargetRequest())) + bool isMetal = isMetalTarget(context->getTargetRequest()); + if (!isD3DTarget(context->getTargetRequest()) && !isMetal) return; auto typeLayout = varLayout->typeLayout; @@ -1018,6 +1019,7 @@ static void addExplicitParameterBindings_HLSL( if (kind == LayoutResourceKind::None) continue; + // TODO: need to special-case when this is a `c` register binding... // Find the appropriate resource-binding information @@ -1025,6 +1027,19 @@ static void addExplicitParameterBindings_HLSL( // of the given kind. auto typeRes = typeLayout->FindResourceInfo(kind); + if (isMetal && !typeRes) + { + // Metal doesn't distinguish a unordered access and a readonly/uniform buffer. + switch (kind) + { + case LayoutResourceKind::UnorderedAccess: + case LayoutResourceKind::ShaderResource: + semanticInfo.kind = LayoutResourceKind::MetalBuffer; + typeRes = typeLayout->FindResourceInfo(LayoutResourceKind::MetalBuffer); + break; + } + } + LayoutSize count = 0; if (typeRes) { @@ -1073,7 +1088,7 @@ static void addExplicitParameterBindings_GLSL( // so that we are able to distinguish between // Vulkan and OpenGL as targets. // - if (!isKhronosTarget(context->getTargetRequest())) + if (!isKhronosTarget(context->getTargetRequest()) && !isWGPUTarget(context->getTargetRequest())) return; auto typeLayout = varLayout->typeLayout; diff --git a/tests/bugs/gh-471.slang b/tests/bugs/gh-471.slang index e7b09760b..d59f97281 100644 --- a/tests/bugs/gh-471.slang +++ b/tests/bugs/gh-471.slang @@ -22,7 +22,7 @@ int test(int inVal) return x * 16; } -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/bugs/gh-775.slang b/tests/bugs/gh-775.slang index d25fa493b..05065e34a 100644 --- a/tests/bugs/gh-775.slang +++ b/tests/bugs/gh-775.slang @@ -18,7 +18,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[9 9 9 9], stride=4):out,name outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/bugs/static-method.slang b/tests/bugs/static-method.slang index 98e4a3273..4e5785c41 100644 --- a/tests/bugs/static-method.slang +++ b/tests/bugs/static-method.slang @@ -11,7 +11,7 @@ struct S } //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; int test(int t) { diff --git a/tests/bugs/static-var.slang b/tests/bugs/static-var.slang index 004567466..12ce5cb4d 100644 --- a/tests/bugs/static-var.slang +++ b/tests/bugs/static-var.slang @@ -8,7 +8,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[9 9 9 9], stride=4):out,name outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/bugs/texture2d-gather.hlsl b/tests/bugs/texture2d-gather.hlsl index 9b0607eae..7344d863d 100644 --- a/tests/bugs/texture2d-gather.hlsl +++ b/tests/bugs/texture2d-gather.hlsl @@ -3,8 +3,9 @@ //TEST_INPUT: Texture2D(size=16, content=chessboard, format=R32_FLOAT):name g_texture //TEST_INPUT: Sampler :name g_sampler -Texture2D g_texture : register(t0); -SamplerState g_sampler : register(s0); +Texture2D g_texture; + +SamplerState g_sampler; cbuffer Uniforms { diff --git a/tests/bugs/type-legalize-bug-1.slang b/tests/bugs/type-legalize-bug-1.slang index 7f1e99d37..c2e2d4565 100644 --- a/tests/bugs/type-legalize-bug-1.slang +++ b/tests/bugs/type-legalize-bug-1.slang @@ -5,7 +5,7 @@ //TEST_INPUT:type_conformance A:IFoo=0 //TEST_INPUT:type_conformance B:IFoo=1 -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; interface IFoo { associatedtype T : IFoo; diff --git a/tests/compute/break-stmt.slang b/tests/compute/break-stmt.slang index b0c8a666c..40bed3af3 100644 --- a/tests/compute/break-stmt.slang +++ b/tests/compute/break-stmt.slang @@ -16,7 +16,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/continue-stmt.slang b/tests/compute/continue-stmt.slang index 50d8141bc..e730a7d28 100644 --- a/tests/compute/continue-stmt.slang +++ b/tests/compute/continue-stmt.slang @@ -21,7 +21,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/default-initializer.slang b/tests/compute/default-initializer.slang index dab195d25..9f9eb22e2 100644 --- a/tests/compute/default-initializer.slang +++ b/tests/compute/default-initializer.slang @@ -23,7 +23,7 @@ int test(int value) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/explicit-this-expr.slang b/tests/compute/explicit-this-expr.slang index baf718be0..9eedf5fe9 100644 --- a/tests/compute/explicit-this-expr.slang +++ b/tests/compute/explicit-this-expr.slang @@ -16,7 +16,7 @@ struct A }; //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; float test(float inVal) diff --git a/tests/compute/generics-constrained.slang b/tests/compute/generics-constrained.slang index 435b840d1..04a9c59b7 100644 --- a/tests/compute/generics-constrained.slang +++ b/tests/compute/generics-constrained.slang @@ -28,6 +28,7 @@ float testHelp(T helper) } //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +[vk::binding(0, 0)] RWStructuredBuffer outputBuffer : register(u0); diff --git a/tests/compute/global-init.slang b/tests/compute/global-init.slang index fd7937cf0..d2f7bb41e 100644 --- a/tests/compute/global-init.slang +++ b/tests/compute/global-init.slang @@ -12,7 +12,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/implicit-generic-app.slang b/tests/compute/implicit-generic-app.slang index c41b5e31d..b00f25877 100644 --- a/tests/compute/implicit-generic-app.slang +++ b/tests/compute/implicit-generic-app.slang @@ -30,7 +30,7 @@ int test(int val) } //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/implicit-this-expr.slang b/tests/compute/implicit-this-expr.slang index e8b720093..ed80a9251 100644 --- a/tests/compute/implicit-this-expr.slang +++ b/tests/compute/implicit-this-expr.slang @@ -15,7 +15,7 @@ struct A }; //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; float test(float inVal) { diff --git a/tests/compute/init-list-defaults.slang b/tests/compute/init-list-defaults.slang index 049450109..fe55e9259 100644 --- a/tests/compute/init-list-defaults.slang +++ b/tests/compute/init-list-defaults.slang @@ -24,7 +24,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[9 9 9 9], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/inout.slang b/tests/compute/inout.slang index 186bcd3db..5d1afaa79 100644 --- a/tests/compute/inout.slang +++ b/tests/compute/inout.slang @@ -36,7 +36,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/multiple-continue-sites.slang b/tests/compute/multiple-continue-sites.slang index cf5033c9e..6ee24ca8d 100644 --- a/tests/compute/multiple-continue-sites.slang +++ b/tests/compute/multiple-continue-sites.slang @@ -28,7 +28,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/struct-default-init.slang b/tests/compute/struct-default-init.slang index dc0e0218a..236fff57f 100644 --- a/tests/compute/struct-default-init.slang +++ b/tests/compute/struct-default-init.slang @@ -26,7 +26,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[9 9 9 9], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/switch-stmt.slang b/tests/compute/switch-stmt.slang index f0de612cb..b8411a9ad 100644 --- a/tests/compute/switch-stmt.slang +++ b/tests/compute/switch-stmt.slang @@ -30,7 +30,7 @@ int test(int inVal) } //TEST_INPUT:ubuffer(data=[0 1 2 3 4 5 6 7], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(8, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/this-type.slang b/tests/compute/this-type.slang index 94f42ce53..21e5ddeae 100644 --- a/tests/compute/this-type.slang +++ b/tests/compute/this-type.slang @@ -36,7 +36,7 @@ int test(int value) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/compute/user-defined-initializer.slang b/tests/compute/user-defined-initializer.slang index dcd94f68a..0a1dfe328 100644 --- a/tests/compute/user-defined-initializer.slang +++ b/tests/compute/user-defined-initializer.slang @@ -28,7 +28,7 @@ int test(int value) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/language-feature/properties/property-in-interface.slang b/tests/language-feature/properties/property-in-interface.slang index 50a9c7f2e..9f9e7ff50 100644 --- a/tests/language-feature/properties/property-in-interface.slang +++ b/tests/language-feature/properties/property-in-interface.slang @@ -46,7 +46,7 @@ int test(int value) } //TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) diff --git a/tests/preprocessor/line-macro.slang b/tests/preprocessor/line-macro.slang index 2b035357a..609d6cf5a 100644 --- a/tests/preprocessor/line-macro.slang +++ b/tests/preprocessor/line-macro.slang @@ -1,7 +1,7 @@ //TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK): -output-using-type //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; #define T(x) x #define LL T(__LINE__) diff --git a/tests/serialization/std-lib-serialize.slang b/tests/serialization/std-lib-serialize.slang index e63a1220e..3dfda3ec5 100644 --- a/tests/serialization/std-lib-serialize.slang +++ b/tests/serialization/std-lib-serialize.slang @@ -12,7 +12,7 @@ struct A }; //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer -RWStructuredBuffer outputBuffer : register(u0); +RWStructuredBuffer outputBuffer; float test(float inVal) diff --git a/tests/wgsl/explicit-binding.slang b/tests/wgsl/explicit-binding.slang new file mode 100644 index 000000000..d2cf73a69 --- /dev/null +++ b/tests/wgsl/explicit-binding.slang @@ -0,0 +1,23 @@ +//TEST:SIMPLE(filecheck=METAL): -target metal +//TEST:SIMPLE(filecheck=CHECK): -target wgsl -entry computeMain -stage compute + +// CHECK-DAG: @binding(9) @group(7) +// CHECK-DAG: @binding(3) @group(4) +// CHECK-DAG: @binding(1) @group(2) + +// METAL-DAG: buffer(9) +// METAL-DAG: texture(7) + +[vk::binding(1, 2)] +Texture2D texA : register(t7); + +[vk::binding(3, 4)] +ConstantBuffer cb; + +RWStructuredBuffer ob : register(u9, space7); + +[numthreads(1,1,1)] +void computeMain() +{ + ob[0] = cb + texA.Load(int3(0)).x; +} \ No newline at end of file -- cgit v1.2.3