diff options
| author | Darren Wihandi <65404740+fairywreath@users.noreply.github.com> | 2025-05-25 12:58:08 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-25 09:58:08 -0700 |
| commit | 0476b57faad96bee61f59f27ddd48c6cb067cfa2 (patch) | |
| tree | d3fe49cd906c29b03b2a840dd2c057ccc331b4f7 | |
| parent | 554be7a5f990df19a21db10b4e5dc0285cbe8168 (diff) | |
Add full support for SPV_NV_shader_subgroup_partitioned (#7103)
* Properly implement WaveMask* variants of WaveMultiPrefix* intrinsics
* More partitioned intrinsics
* More partitioned intrinsics and cleaned up non-prefixed WaveMask* implementations
* Refactor HLSL WaveMultiPrefix* implementations
* fix cap atoms
* Clean up implementation
* Add GLSL intrinsics and cleanup
* Add tests
* Fix affected capability test
* Update and fix tests
* Move expected.txt file
* Refactor WaveMask* to call WaveMulti*
* Refactor SPIRV/GLSL preamble code
* Enable emit-via-glsl tests
* remove wave_multi_prefix capability in favor of subgroup_partitioned
* Update docs
* Update cap atoms doc
23 files changed, 2137 insertions, 774 deletions
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md index d0e94a622..4ecaac546 100644 --- a/docs/command-line-slangc-reference.md +++ b/docs/command-line-slangc-reference.md @@ -1306,7 +1306,6 @@ A capability describes an optional feature that a target may or may not support. * `atomicfloat2` * `fragmentshaderbarycentric` * `shadermemorycontrol` -* `wave_multi_prefix` * `bufferreference` * `bufferreference_int64` * `cooperative_vector` diff --git a/docs/user-guide/a3-02-reference-capability-atoms.md b/docs/user-guide/a3-02-reference-capability-atoms.md index 9c2f6ca0a..0766fdf82 100644 --- a/docs/user-guide/a3-02-reference-capability-atoms.md +++ b/docs/user-guide/a3-02-reference-capability-atoms.md @@ -964,9 +964,6 @@ Compound Capabilities `shadermemorycontrol` > (gfx targets) Capabilities needed to use memory barriers -`wave_multi_prefix` -> Capabilities needed to use HLSL tier wave operations - `bufferreference` > Capabilities needed to use GLSL buffer-reference's diff --git a/docs/wave-intrinsics.md b/docs/wave-intrinsics.md index 7f6fb7b77..b434666ab 100644 --- a/docs/wave-intrinsics.md +++ b/docs/wave-intrinsics.md @@ -1,9 +1,13 @@ + Wave Intrinsics =============== -Slang has support for Wave intrinsics introduced to HLSL in [SM6.0](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12) and [SM6.5](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/HLSL_ShaderModel6_5.md). All intrinsics are available on D3D12, and a subset on Vulkan. +Slang has support for Wave intrinsics introduced to HLSL in [SM6.0](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12) and [SM6.5](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/HLSL_ShaderModel6_5.md). All intrinsics are available on D3D12 and Vulkan. + +On GLSL targets such as Vulkan wave intrinsics map to ['subgroup' extension] (https://github.com/KhronosGroup/GLSL/blob/master/extensions/khr/GL_KHR_shader_subgroup.txt). Vulkan supports a number of masked wave operations through `SPV_NV_shader_subgroup_partitioned` that are not supported by HLSL. + +There is no subgroup support for Matrix types, and currently this means that Matrix is not a supported type for Wave intrinsics on Vulkan, but may be in the future. -On GLSL targets such as Vulkan wave intrinsics map to ['subgroup' extension] (https://github.com/KhronosGroup/GLSL/blob/master/extensions/khr/GL_KHR_shader_subgroup.txt). There is no subgroup support for Matrix types, and currently this means that Matrix is not a supported type for Wave intrinsics on Vulkan, but may be in the future. Also introduced are some 'non standard' Wave intrinsics which are only available on Slang. All WaveMask intrinsics are non standard. Other non standard intrinsics expose more accurately different behaviours which are either not distinguished on HLSL, or perhaps currently unavailable. Two examples would be `WaveShuffle` and `WaveBroadcastLaneAt`. @@ -31,7 +35,10 @@ Using WaveMask intrinsics is generally more verbose and prone to error than the * Might allow for higher performance (for example it gives more control of divergence) * Maps most closely to CUDA -On D3D12 and Vulkan the WaveMask intrinsics can be used, but the mask is effectively ignored. For this to work across targets including CUDA, the mask must be calculated such that it exactly matches that of HLSL defined 'active' lanes, else the behavior is undefined. +For this to work across targets including CUDA, the mask must be calculated such that it exactly matches that of HLSL defined 'active' lanes, else the behavior is undefined. + +On D3D12 and Vulkan the WaveMask intrinsics can be used, but the mask may be ignored depending on target's support for partitioned/masked wave intrinsics. SPIRV provides support for a wide variety of operations through the `SPV_NV_shader_subgroup_partitioned` extension while HLSL only provides a small subset of operations through `WaveMultiPrefix*` intrinsics. The difference between Slang's `WaveMask` and these targets' partitioned wave intrinsics is that they accept a `uint4` mask instead of a `uint` mask. `WaveMask*` intrinsics effectively gets translated to `WaveMulti*` intrinsics when targeting SPIRV/GLSL and HLSL. Please consult [Wave Multi Intrinsics](#wave-multi-intrinsics) for more details, including what masked operations are supported by each target. + The WaveMask intrinsics are a non standard Slang feature, and may change in the future. @@ -103,10 +110,10 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) outputBuffer[idx] = value; } ``` - ## WaveMulti -The standard 'Multi' intrinsics were added to HLSL is SM6.5, they can specify a mask of lanes via uint4. They introduce some intrinsics that work in a similar fashion to the `WaveMask` intrinsics. The available intrisnics is currently significantly restricted compared to WaveMask. +The standard 'Multi' intrinsics were added to HLSL is SM 6.5 and are available in SPIRV through `SPV_NV_shader_subgroup_partitioned`, they can specify a mask of lanes via uint4. SPIRV provide non-prefix (reduction) and prefix (scan) intrinsics for arithmetic and min/max operations, while HLSL only provides a subset of these, namely exclusive prefix arithmetic operations. + Standard Wave intrinsics ========================= @@ -236,6 +243,7 @@ void GroupMemoryBarrierWithWaveSync(); Synchronizes all lanes to the same GroupMemoryBarrierWithWaveSync in program flow. Orders group shared memory accesses such that accesses after the barrier can be seen by writes before. + Wave Rotate Intrinsics ====================== @@ -250,16 +258,77 @@ T WaveRotate(T value, uint delta); T WaveClusteredRotate(T value, uint delta, constexpr uint clusterSize); ``` +Wave Multi Intrinsics +====================== + +`WaveMulti` intrinsics take an explicit `uint4` mask of lanes to operate on. They correspond to the subgroup partitioned intrinsics provided by `SPV_NV_shader_subgroup_partitioned` and the `WaveMultiPrefix*` intrinsics provided by HLSL SM 6.5. HLSL's `WaveMulti*` intrinsics only provide operations for exclusive prefix arithmetic operations, while Vulkan's `SPV_NV_shader_subgroup_partitioned` provides operations for both inclusive/exclusive prefix (scan) and non-prefix (reduction) arithmetic and min/max operations. + +Slang adds new `WaveMulti*` intrinsics in addition to HLSL's `WaveMultiPrefix*` to allow generating all partitioned intrinsics supported in SPIRV. The new, non-standard HLSL, `WaveMulti*` intrinsics are only supported when targeting SPIRV, GLSL and CUDA. The inclusive variants of HLSL's `WaveMultiPrefix*` intrinsics are emulated by Slang by performing an additional operation in the current invocation. Metal and WGSL targets do not support `WaveMulti` intrinsics. +``` +// Across lane ops. These are only supported when targeting SPIRV, GLSL and CUDA. + +T WaveMultiSum(T value, uint4 mask); + +T WaveMultiProduct(T value, uint4 mask); + +T WaveMultiMin(T value, uint4 mask); + +T WaveMultiMax(T value, uint4 mask); + +T WaveMultiBitAnd(T value, uint4 mask); + +T WaveMultiBitOr(T value, uint4 mask); + +T WaveMultiBitXor(T value, uint4 mask); + + +// Prefix arithmetic operations. Supported when targeting SPIRV, GLSL, CUDA and HLSL. +// In addition to these non-HLSL standard intrinsics are the standard `WaveMultiPrefix*` +// intrinsics provided by SM 6.5, detailed in the `Standard Wave Intrinsics` section. + +T WaveMultiPrefixInclusiveSum(T value, uint4 mask); + +T WaveMultiPrefixInclusiveProduct(T value, uint4 mask); + +T WaveMultiPrefixInclusiveBitAnd(T value, uint4 mask); + +T WaveMultiPrefixInclusiveBitOr(T value, uint4 mask); + +T WaveMultiPrefixInclusiveBitXor(T value, uint4 mask); + +T WaveMultiPrefixExclusiveSum(T value, uint4 mask); + +T WaveMultiPrefixExclusiveProduct(T value, uint4 mask); + +T WaveMultiPrefixExclusiveBitAnd(T value, uint4 mask); + +T WaveMultiPrefixExclusiveBitOr(T value, uint4 mask); + +T WaveMultiPrefixExclusiveBitXor(T value, uint4 mask); + + +// Prefix min/max operations. Supported when targeting SPIRV and GLSL. + +T WaveMultiPrefixInclusiveMin(T value, uint4 mask); + +T WaveMultiPrefixInclusiveMax(T value, uint4 mask); + +T WaveMultiPrefixExclusiveMin(T value, uint4 mask); + +T WaveMultiPrefixExclusiveMax(T value, uint4 mask); +``` + + Wave Mask Intrinsics ==================== CUDA has a different programming model for inter warp/wave communication based around masks of active lanes. This is because the CUDA programming model allows for divergence that is more granualar than just on program flow, and that there isn't implied reconvergence at the end of a conditional. -In the future Slang may have the capability to work out the masks required such that the regular HLSL Wave intrinsics work. As it stands there does not appear to be any way to implement the regular Wave intrinsics directly. To work around this problem we introduce 'WaveMask' intrinsics, which are essentially the same as the regular HLSL Wave intrinsics with the first parameter as the WaveMask which identifies the participating lanes. +In the future Slang may have the capability to work out the masks required such that the regular HLSL Wave intrinsics work. As it stands there does not appear to be any way to implement the regular Wave intrinsics directly. To work around this problem we introduce 'WaveMask' intrinsics, which are essentially the same as the regular HLSL Wave intrinsics with the first parameter as the WaveMask which identifies the participating lanes. -The WaveMask intrinsics will work across targets, but *only* if on CUDA targets the mask captures exactly the same lanes as the 'Active' lanes concept in HLSL. If the masks deviate then the behavior is undefined. On non CUDA based targets currently the mask is ignored. This behavior may change on GLSL which has an extension to support a more CUDA like behavior. +The WaveMask intrinsics will work across targets, but *only* if on CUDA targets the mask captures exactly the same lanes as the 'Active' lanes concept in HLSL. If the masks deviate then the behavior is undefined. On non CUDA based targets currently the mask *may* be ignored depending on the intrinsics supported by the target. -Most of the `WaveMask` functions are identical to the regular Wave intrinsics, but they take a WaveMask as the first parameter, and the intrinsic name starts with `WaveMask`. +Most of the `WaveMask` functions are identical to the regular Wave intrinsics, but they take a WaveMask as the first parameter, and the intrinsic name starts with `WaveMask`. Also note that the `WaveMask` functions are introduced in Slang before the `WaveMulti` intrinsics, and they effectively function the same other than the mask width in bits (`uint` vs `uint4`). The `WaveMulti` intrinsics map closer to SPIRV and HLSL, and are recommended to be used over `WaveMask` intrinsics whenever possible. We plan to deprecate the `WaveMask` intrinsics some time in the future. ``` WaveMask WaveGetConvergedMask(); diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang index 588396251..88c90a777 100644 --- a/source/slang/glsl.meta.slang +++ b/source/slang/glsl.meta.slang @@ -8280,6 +8280,7 @@ public vector<T,N> subgroupQuadSwapDiagonal(vector<T,N> value) // GL_KHR_shader_subgroup_rotate __generic<T : __BuiltinType> +[ForceInline] [require(glsl_metal_spirv, subgroup_rotate)] public T subgroupRotate(T value, uint delta) { @@ -8287,6 +8288,7 @@ public T subgroupRotate(T value, uint delta) } __generic<T : __BuiltinType, let N : int> +[ForceInline] [require(glsl_metal_spirv, subgroup_rotate)] public vector<T, N> subgroupRotate(vector<T, N> value, uint delta) { @@ -8294,6 +8296,7 @@ public vector<T, N> subgroupRotate(vector<T, N> value, uint delta) } __generic<T : __BuiltinType> +[ForceInline] [require(glsl_spirv, subgroup_rotate)] public T subgroupClusteredRotate(T value, uint delta, constexpr uint clusterSize) { @@ -8302,12 +8305,360 @@ public T subgroupClusteredRotate(T value, uint delta, constexpr uint clusterSize } __generic<T : __BuiltinType, let N : int> +[ForceInline] [require(glsl_spirv, subgroup_rotate)] public vector<T, N> subgroupClusteredRotate(vector<T, N> value, uint delta, constexpr uint clusterSize) { return WaveClusteredRotate(value, delta, clusterSize); } + +// GL_NV_shader_subgroup_partitioned + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedAddNV(T value, uvec4 ballot) +{ + return WaveMultiSum(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedAddNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiSum(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedMulNV(T value, uvec4 ballot) +{ + return WaveMultiProduct(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedMulNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiProduct(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedMinNV(T value, uvec4 ballot) +{ + return WaveMultiMin(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N: int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedMinNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiMin(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedMaxNV(T value, uvec4 ballot) +{ + return WaveMultiMax(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedMaxNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiMax(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedAndNV(T value, uvec4 ballot) +{ + return WaveMultiBitAnd(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedAndNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiBitAnd(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedOrNV(T value, uvec4 ballot) +{ + return WaveMultiBitOr(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedOrNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiBitOr(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedXorNV(T value, uvec4 ballot) +{ + return WaveMultiBitXor(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedXorNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiBitXor(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveAddNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveSum(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveAddNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveSum(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveMulNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveProduct(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveMulNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveProduct(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveMinNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveMin(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveMinNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveMin(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveMaxNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveMax(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveMaxNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveMax(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveAndNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveBitAnd(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveAndNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveBitAnd(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveOrNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveBitOr(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveOrNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveBitOr(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedInclusiveXorNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveBitXor(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedInclusiveXorNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixInclusiveBitXor(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveAddNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveSum(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveAddNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveSum(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveMulNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveProduct(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveMulNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveProduct(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveMinNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveMin(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveMinNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveMin(value, ballot); +} + +__generic<T : __BuiltinArithmeticType> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveMaxNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveMax(value, ballot); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveMaxNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveMax(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveAndNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveBitAnd(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveAndNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveBitAnd(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveOrNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveBitOr(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveOrNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveBitOr(value, ballot); +} + +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public T subgroupPartitionedExclusiveXorNV(T value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveBitXor(value, ballot); +} + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public vector<T, N> subgroupPartitionedExclusiveXorNV(vector<T, N> value, uvec4 ballot) +{ + return WaveMultiPrefixExclusiveBitXor(value, ballot); +} + +__generic<T : __BuiltinType> +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +public uvec4 subgroupPartitionNV(T value) +{ + return WaveMatch(value); +} + + //// GLSL atomic // The following type internally is a Shader Storage Buffer diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 87f98adaf..cb050dd51 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -14138,382 +14138,294 @@ uint WaveMaskPrefixCountBits(WaveMask mask, bool value) // Across lane ops -__generic<T : __BuiltinIntegerType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskBitAnd(WaveMask mask, T expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupAnd($1)"; - case cuda: __intrinsic_asm "_waveAnd($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitAnd($1)"; - case spirv: - return spirv_asm { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformBitwiseAnd $$T result Subgroup 0 $expr - }; + case hlsl: + __intrinsic_asm "WaveActiveBitAnd($1)"; + default: + return WaveMultiBitAnd(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskBitAnd(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupAnd($1)"; - case cuda: __intrinsic_asm "_waveAndMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitAnd($1)"; - case spirv: - return spirv_asm { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformBitwiseAnd $$vector<T,N> result Subgroup 0 $expr - }; + case hlsl: + __intrinsic_asm "WaveActiveBitAnd($1)"; + default: + return WaveMultiBitAnd(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskBitAnd(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveAndMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitAnd($1)"; + case hlsl: + __intrinsic_asm "WaveActiveBitAnd($1)"; + default: + return WaveMultiBitAnd(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskBitOr(WaveMask mask, T expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupOr($1)"; - case cuda: __intrinsic_asm "_waveOr($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitOr($1)"; - case spirv: - return spirv_asm { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformBitwiseOr $$T result Subgroup 0 $expr - }; + case hlsl: + __intrinsic_asm "WaveActiveBitOr($1)"; + default: + return WaveMultiBitOr(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int> +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +[ForceInline] vector<T,N> WaveMaskBitOr(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupOr($1)"; - case cuda: __intrinsic_asm "_waveOrMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitOr($1)"; - case spirv: - return spirv_asm { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformBitwiseOr $$vector<T,N> result Subgroup 0 $expr - }; + case hlsl: + __intrinsic_asm "WaveActiveBitOr($1)"; + default: + return WaveMultiBitOr(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] + +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskBitOr(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveOrMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitOr($1)"; + case hlsl: + __intrinsic_asm "WaveActiveBitOr($1)"; + default: + return WaveMultiBitOr(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskBitXor(WaveMask mask, T expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupXor($1)"; - case cuda: __intrinsic_asm "_waveXor($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitXor($1)"; - case spirv: - return spirv_asm { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformBitwiseXor $$T result Subgroup 0 $expr - }; + case hlsl: + __intrinsic_asm "WaveActiveBitXor($1)"; + default: + return WaveMultiBitXor(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] + +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskBitXor(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupXor($1)"; - case cuda: __intrinsic_asm "_waveXorMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitXor($1)"; - case spirv: - return spirv_asm { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformBitwiseXor $$vector<T,N> result Subgroup 0 $expr - }; + case hlsl: + __intrinsic_asm "WaveActiveBitXor($1)"; + default: + return WaveMultiBitXor(expr, uint4(mask, 0, 0, 0)); } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] + +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskBitXor(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveXorMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveBitXor($1)"; + case hlsl: + __intrinsic_asm "WaveActiveBitXor($1)"; + default: + return WaveMultiBitXor(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskMax(WaveMask mask, T expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupMax($1)"; - case cuda: __intrinsic_asm "_waveMax($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveMax($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMax $$T result Subgroup 0 $expr}; - else if (__isSignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformSMax $$T result Subgroup 0 $expr}; - else if (__isUnsignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformUMax $$T result Subgroup 0 $expr}; - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveMax($1)"; + default: + return WaveMultiMax(expr, uint4(mask, 0, 0, 0)); } } + __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskMax(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupMax($1)"; - case cuda: __intrinsic_asm "_waveMaxMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveMax($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMax $$vector<T,N> result Subgroup 0 $expr}; - else if (__isSignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformSMax $$vector<T,N> result Subgroup 0 $expr}; - else if (__isUnsignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformUMax $$vector<T,N> result Subgroup 0 $expr}; - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveMax($1)"; + default: + return WaveMultiMax(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskMax(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveMaxMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveMax($1)"; + case hlsl: + __intrinsic_asm "WaveActiveMax($1)"; + default: + return WaveMultiMax(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskMin(WaveMask mask, T expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupMin($1)"; - case cuda: __intrinsic_asm "_waveMin($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveMin($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMin $$T result Subgroup 0 $expr}; - else if (__isSignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformSMin $$T result Subgroup 0 $expr}; - else if (__isUnsignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformUMin $$T result Subgroup 0 $expr}; - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveMin($1)"; + default: + return WaveMultiMin(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskMin(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupMin($1)"; - case cuda: __intrinsic_asm "_waveMinMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveMin($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMin $$vector<T,N> result Subgroup 0 $expr}; - else if (__isSignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformSMin $$vector<T,N> result Subgroup 0 $expr}; - else if (__isUnsignedInt<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformUMin $$vector<T,N> result Subgroup 0 $expr}; - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveMin($1)"; + default: + return WaveMultiMin(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskMin(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveMinMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveMin($1)"; + case hlsl: + __intrinsic_asm "WaveActiveMin($1)"; + default: + return WaveMultiMin(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskProduct(WaveMask mask, T expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupMul($1)"; - case cuda: __intrinsic_asm "_waveProduct($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveProduct($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMul $$T result Subgroup 0 $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformIMul $$T result Subgroup 0 $expr; - }; - } - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveProduct($1)"; + default: + return WaveMultiProduct(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskProduct(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: __intrinsic_asm "subgroupMul($1)"; - case cuda: __intrinsic_asm "_waveProductMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveProduct($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMul $$vector<T,N> result Subgroup 0 $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformIMul $$vector<T,N> result Subgroup 0 $expr; - }; - } - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveProduct($1)"; + default: + return WaveMultiProduct(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskProduct(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveProductMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveProduct($1)"; + case hlsl: + __intrinsic_asm "WaveActiveProduct($1)"; + default: + return WaveMultiProduct(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskSum(WaveMask mask, T expr) { __target_switch { - case glsl: - if (__isHalf<T>()) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - __intrinsic_asm "subgroupAdd($1)"; - case cuda: __intrinsic_asm "_waveSum($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveSum($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFAdd $$T result Subgroup 0 $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformIAdd $$T result Subgroup 0 $expr; - }; - } - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveSum($1)"; + default: + return WaveMultiSum(expr, uint4(mask, 0, 0, 0)); } } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskSum(WaveMask mask, vector<T,N> expr) { __target_switch { - case glsl: - if (__isHalf<T>()) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - __intrinsic_asm "subgroupAdd($1)"; - case cuda: __intrinsic_asm "_waveSumMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveSum($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFAdd $$vector<T,N> result Subgroup 0 $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformIAdd $$vector<T,N> result Subgroup 0 $expr; - }; - } - else return expr; + case hlsl: + __intrinsic_asm "WaveActiveSum($1)"; + default: + return WaveMultiSum(expr, uint4(mask, 0, 0, 0)); } } + __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskSum(WaveMask mask, matrix<T,N,M> expr) { __target_switch { - case cuda: __intrinsic_asm "_waveSumMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveActiveSum($1)"; + case hlsl: + __intrinsic_asm "WaveActiveSum($1)"; + default: + return WaveMultiSum(expr, uint4(mask, 0, 0, 0)); } } @@ -14580,134 +14492,48 @@ bool WaveMaskAllEqual(WaveMask mask, matrix<T,N,M> value) // Prefix __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskPrefixProduct(WaveMask mask, T expr) { - __target_switch - { - case glsl: - if (__isHalf<T>()) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - __intrinsic_asm "subgroupExclusiveMul($1)"; - case cuda: __intrinsic_asm "_wavePrefixProduct($0, $1)"; - case hlsl: __intrinsic_asm "WavePrefixProduct($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMul $$T result Subgroup ExclusiveScan $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformIMul $$T result Subgroup ExclusiveScan $expr; - }; - } - else return expr; - } + return WaveMultiPrefixProduct(expr, uint4(mask, 0, 0, 0)); } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskPrefixProduct(WaveMask mask, vector<T,N> expr) { - __target_switch - { - case glsl: - if (__isHalf<T>()) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - __intrinsic_asm "subgroupExclusiveMul($1)"; - case cuda: __intrinsic_asm "_wavePrefixProductMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WavePrefixProduct($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFMul $$vector<T,N> result Subgroup ExclusiveScan $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - OpGroupNonUniformIMul $$vector<T,N> result Subgroup ExclusiveScan $expr; - }; - } - else return expr; - } + return WaveMultiPrefixProduct(expr, uint4(mask, 0, 0, 0)); } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskPrefixProduct(WaveMask mask, matrix<T,N,M> expr) { - __target_switch - { - case cuda: __intrinsic_asm "_wavePrefixProductMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WavePrefixProduct($1)"; - } + return WaveMultiPrefixProduct(expr, uint4(mask, 0, 0, 0)); } __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskPrefixSum(WaveMask mask, T expr) { - __target_switch - { - case glsl: - if (__isHalf<T>()) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - __intrinsic_asm "subgroupExclusiveAdd($1)"; - case cuda: __intrinsic_asm "_wavePrefixSum($0, $1)"; - case hlsl: __intrinsic_asm "WavePrefixSum($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFAdd $$T result Subgroup ExclusiveScan $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - result:$$T = OpGroupNonUniformIAdd Subgroup ExclusiveScan $expr; - }; - } - else return expr; - } + return WaveMultiPrefixSum(expr, uint4(mask, 0, 0, 0)); } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskPrefixSum(WaveMask mask, vector<T,N> expr) { - __target_switch - { - case glsl: - if (__isHalf<T>()) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - __intrinsic_asm "subgroupExclusiveAdd($1)"; - case cuda: __intrinsic_asm "_wavePrefixSumMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WavePrefixSum($1)"; - case spirv: - if (__isFloat<T>()) - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformFAdd $$vector<T,N> result Subgroup ExclusiveScan $expr}; - else if (__isInt<T>()) - { - return spirv_asm - { - OpCapability GroupNonUniformArithmetic; - result:$$vector<T,N> = OpGroupNonUniformIAdd Subgroup ExclusiveScan $expr; - }; - } - else return expr; - } + return WaveMultiPrefixSum(expr, uint4(mask, 0, 0, 0)); } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskPrefixSum(WaveMask mask, matrix<T,N,M> expr) { - __target_switch - { - case cuda: __intrinsic_asm "_wavePrefixSumMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WavePrefixSum($1)"; - } + return WaveMultiPrefixSum(expr, uint4(mask, 0, 0, 0)); } __generic<T : __BuiltinType> @@ -14813,133 +14639,76 @@ WaveMask WaveMaskMatch(WaveMask mask, matrix<T,N,M> value) } } -__generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskPrefixBitAnd(WaveMask mask, T expr) { - __target_switch - { - case glsl: __intrinsic_asm "subgroupExclusiveAnd($1)"; - case cuda: __intrinsic_asm "_wavePrefixAnd($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitAnd($1, uint4($0, 0, 0, 0))"; - case spirv: - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformBitwiseAnd $$T result Subgroup ExclusiveScan $expr}; - } + return WaveMultiPrefixBitAnd(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskPrefixBitAnd(WaveMask mask, vector<T,N> expr) { - __target_switch - { - case glsl: __intrinsic_asm "subgroupExclusiveAnd($1)"; - case cuda: __intrinsic_asm "_wavePrefixAndMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitAnd($1, uint4($0, 0, 0, 0))"; - case spirv: - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformBitwiseAnd $$vector<T,N> result Subgroup ExclusiveScan $expr}; - } + return WaveMultiPrefixBitAnd(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[ForceInline] +[require(cuda_hlsl, subgroup_partitioned)] matrix<T,N,M> WaveMaskPrefixBitAnd(WaveMask mask, matrix<T,N,M> expr) { - __target_switch - { - case cuda: __intrinsic_asm "_wavePrefixAndMultiple(_getMultiPrefixMask($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitAnd($1, uint4($0, 0, 0, 0))"; - } + return WaveMultiPrefixBitAnd(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskPrefixBitOr(WaveMask mask, T expr) { - __target_switch - { - case glsl: __intrinsic_asm "subgroupExclusiveOr($1)"; - case cuda: __intrinsic_asm "_wavePrefixOr($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitOr($1, uint4($0, 0, 0, 0))"; - case spirv: - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformBitwiseAnd $$T result Subgroup ExclusiveScan $expr}; - } + return WaveMultiPrefixBitOr(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskPrefixBitOr(WaveMask mask, vector<T,N> expr) { - __target_switch - { - case glsl: __intrinsic_asm "subgroupExclusiveOr($1)"; - case cuda: __intrinsic_asm "_wavePrefixOrMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitOr($1, uint4($0, 0, 0, 0))"; - case spirv: - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformBitwiseOr $$vector<T,N> result Subgroup ExclusiveScan $expr}; - } + return WaveMultiPrefixBitOr(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskPrefixBitOr(WaveMask mask, matrix<T,N,M> expr) { - __target_switch - { - case cuda: __intrinsic_asm "_wavePrefixOrMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitOr($1, uint4($0, 0, 0, 0))"; - } + return WaveMultiPrefixBitOr(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] T WaveMaskPrefixBitXor(WaveMask mask, T expr) { - __target_switch - { - case glsl: __intrinsic_asm "subgroupExclusiveXor($1)"; - case cuda: __intrinsic_asm "_wavePrefixXor($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitXor($1, uint4($0, 0, 0, 0))"; - case spirv: - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformBitwiseXor $$T result Subgroup ExclusiveScan $expr}; - } + return WaveMultiPrefixBitXor(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_arithmetic) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] vector<T,N> WaveMaskPrefixBitXor(WaveMask mask, vector<T,N> expr) { - __target_switch - { - case glsl: __intrinsic_asm "subgroupExclusiveXor($1)"; - case cuda: __intrinsic_asm "_wavePrefixXorMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitXor($1, uint4($0, 0, 0, 0))"; - case spirv: - return spirv_asm {OpCapability GroupNonUniformArithmetic; OpGroupNonUniformBitwiseXor $$vector<T,N> result Subgroup ExclusiveScan $expr}; - } + return WaveMultiPrefixBitOr(expr, uint4(mask, 0, 0, 0)); } -__generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_hlsl, subgroup_arithmetic)] +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] matrix<T,N,M> WaveMaskPrefixBitXor(WaveMask mask, matrix<T,N,M> expr) { - __target_switch - { - case cuda: __intrinsic_asm "_wavePrefixXorMultiple($0, $1)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitXor($1, uint4($0, 0, 0, 0))"; - } + return WaveMultiPrefixBitOr(expr, uint4(mask, 0, 0, 0)); } //@public: @@ -15156,7 +14925,7 @@ const WaveActiveBitOpEntry kWaveActiveBitOpEntries[] = {{"BitAnd", "And", "Bitwi for (auto opName : kWaveActiveBitOpEntries) { }}}} /// @category wave Wave and quad functions -__generic<T : __BuiltinIntegerType> +__generic<T : __BuiltinLogicalType> __glsl_extension(GL_KHR_shader_subgroup_arithmetic) __spirv_version(1.3) __wgsl_extension(subgroups) @@ -15179,7 +14948,7 @@ T WaveActive$(opName.hlslName)(T expr) } } -__generic<T : __BuiltinIntegerType, let N : int> +__generic<T : __BuiltinLogicalType, let N : int> __glsl_extension(GL_KHR_shader_subgroup_arithmetic) __spirv_version(1.3) __wgsl_extension(subgroups) @@ -15202,7 +14971,7 @@ vector<T, N> WaveActive$(opName.hlslName)(vector<T, N> expr) } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> +__generic<T : __BuiltinLogicalType, let N : int, let M : int> [require(cuda_glsl_hlsl_metal_spirv_wgsl, subgroup_arithmetic)] matrix<T, N, M> WaveActive$(opName.hlslName)(matrix<T, N, M> expr) { @@ -16238,7 +16007,7 @@ uint4 WaveMatch(matrix<T,N,M> value) } /// @category wave -[require(cuda_hlsl, wave_multi_prefix)] +[require(cuda_hlsl, subgroup_partitioned)] uint WaveMultiPrefixCountBits(bool value, uint4 mask) { __target_switch @@ -16248,537 +16017,750 @@ uint WaveMultiPrefixCountBits(bool value, uint4 mask) } } -/// @category wave -__generic<T : __BuiltinIntegerType> -__glsl_extension(GL_NV_shader_subgroup_partitioned) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -T WaveMultiPrefixBitAnd(T expr, uint4 mask) +__glsl_extension(GL_EXT_demote_to_helper_invocation) +[ForceInline] +[require(glsl_hlsl_metal_spirv, helper_lane)] +bool IsHelperLane() { - __target_switch - { - case cuda: __intrinsic_asm "_wavePrefixAnd(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitAnd"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveAndNV"; + __target_switch { + case hlsl: __intrinsic_asm "IsHelperLane()"; + case glsl: __intrinsic_asm "gl_HelperInvocation"; + case metal: __intrinsic_asm "simd_is_helper_thread()"; case spirv: - return spirv_asm - { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - result:$$T = OpGroupNonUniformBitwiseAnd Subgroup PartitionedExclusiveScanNV $expr $mask + return spirv_asm { + OpExtension "SPV_EXT_demote_to_helper_invocation"; + OpCapability DemoteToHelperInvocationEXT; + result:$$bool = OpIsHelperInvocationEXT }; } } -__generic<T : __BuiltinIntegerType, let N : int> -__glsl_extension(GL_NV_shader_subgroup_partitioned) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -vector<T,N> WaveMultiPrefixBitAnd(vector<T,N> expr, uint4 mask) +//@hidden: + +__generic<T : __BuiltinType> +[ForceInline] +[require(glsl)] +void __requireGLSLShaderSubgroupTypeExtension() { + // the following is a seperate function call, since else the `__requireTargetExtension` and associated __intrinsic_asm is ignored if the calling function also calls an __intrinsic_asm + if (__type_equals<T, half>() + || __type_equals<T, float16_t>() + ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); + else if (__type_equals<T, uint8_t>() + || __type_equals<T, int8_t>() + ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_int8"); + else if (__type_equals<T, uint16_t>() + || __type_equals<T, int16_t>() + ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_int16"); + else if (__type_equals<T, uint64_t>() + || __type_equals<T, int64_t>() + ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_int64"); + + __intrinsic_asm ""; +} + +__generic<T : __BuiltinType> +[ForceInline] +[require(metal)] +void __checkMetalShaderSubgroupType() +{ + // These builtin types are not supported for Metal's `simd` operations. + if (__type_equals<T, uint8_t>() + || __type_equals<T, int8_t>() + || __type_equals<T, uint64_t>() + || __type_equals<T, int64_t>() + || __isBool<T>() + ) + { + static_assert(false, "Unsupported type for subgroup operations in Metal. Valid types include scalars and vectors of uint/uint32_t, int/int32_t, uint16_t, int16_t, float, and half."); + } +} + +__generic<T : __BuiltinType> +[ForceInline] +void shader_subgroup_preamble() +{ + // checks needed for shader_subgroup functions; __requireTargetExtension does not work + // (does not add the ext specified correctly to the compile output; using extended type + // will result in error for using the type) __target_switch { - case cuda: __intrinsic_asm "_wavePrefixAndMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitAnd"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveAndNV"; - case spirv: - return spirv_asm - { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - result:$$vector<T,N> = OpGroupNonUniformBitwiseAnd Subgroup PartitionedExclusiveScanNV $expr $mask - }; + case glsl: + __requireGLSLShaderSubgroupTypeExtension<T>(); + case metal: + __checkMetalShaderSubgroupType<T>(); + default: + return; } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -matrix<T,N,M> WaveMultiPrefixBitAnd(matrix<T,N,M> expr, uint4 mask) +//@public: + +// +// Wave Rotate intrinsics. +// These are Slang specific intrinsics to rotate values within a subgroup. +// + +__generic<T : __BuiltinType> +__glsl_extension(GL_KHR_shader_subgroup_rotate) +[require(glsl_metal_spirv, subgroup_rotate)] +T WaveRotate(T value, uint delta) { + shader_subgroup_preamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixAndMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitAnd"; case glsl: + __intrinsic_asm "subgroupRotate"; + case metal: + __intrinsic_asm "simd_shuffle_rotate_down"; case spirv: - matrix<T, N, M> result; - for (int i = 0; i < N; ++i) - result[i] = WaveMultiPrefixBitAnd(expr[i], mask); - return result; + return spirv_asm + { + OpExtension "SPV_KHR_subgroup_rotate"; + OpCapability GroupNonUniformRotateKHR; + result:$$T = OpGroupNonUniformRotateKHR Subgroup $value $delta; + }; } } -/// @category wave -__generic<T : __BuiltinIntegerType> -__glsl_extension(GL_NV_shader_subgroup_partitioned) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -T WaveMultiPrefixBitOr(T expr, uint4 mask) +__generic<T : __BuiltinType, let N : int> +__glsl_extension(GL_KHR_shader_subgroup_rotate) +[require(glsl_metal_spirv, subgroup_rotate)] +vector<T, N> WaveRotate(vector<T, N> value, uint delta) { + shader_subgroup_preamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixOr(, _getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitOr"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveOrNV"; + case glsl: + __intrinsic_asm "subgroupRotate"; + case metal: + __intrinsic_asm "simd_shuffle_rotate_down"; case spirv: return spirv_asm { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - result:$$T = OpGroupNonUniformBitwiseOr Subgroup PartitionedExclusiveScanNV $expr $mask + OpExtension "SPV_KHR_subgroup_rotate"; + OpCapability GroupNonUniformRotateKHR; + result:$$vector<T,N> = OpGroupNonUniformRotateKHR Subgroup $value $delta; }; } } -__generic<T : __BuiltinIntegerType, let N : int> -__glsl_extension(GL_NV_shader_subgroup_partitioned) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -vector<T,N> WaveMultiPrefixBitOr(vector<T,N> expr, uint4 mask) +__generic<T : __BuiltinType> +__glsl_extension(GL_KHR_shader_subgroup_rotate) +[require(glsl_spirv, subgroup_rotate)] +T WaveClusteredRotate(T value, uint delta, constexpr uint clusterSize) { + shader_subgroup_preamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixOrMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitOr"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveOrNV"; + case glsl: + __intrinsic_asm "subgroupClusteredRotate"; case spirv: return spirv_asm { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - result:$$vector<T,N> = OpGroupNonUniformBitwiseOr Subgroup PartitionedExclusiveScanNV $expr $mask + OpExtension "SPV_KHR_subgroup_rotate"; + OpCapability GroupNonUniformRotateKHR; + result:$$T = OpGroupNonUniformRotateKHR Subgroup $value $delta $clusterSize; }; } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -matrix<T,N,M> WaveMultiPrefixBitOr(matrix<T,N,M> expr, uint4 mask) +__generic<T : __BuiltinType, let N : int> +__glsl_extension(GL_KHR_shader_subgroup_rotate) +[require(glsl_spirv, subgroup_rotate)] +vector<T, N> WaveClusteredRotate(vector<T, N> value, uint delta, constexpr uint clusterSize) { + shader_subgroup_preamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixOrMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitOr"; case glsl: + __intrinsic_asm "subgroupClusteredRotate"; case spirv: - matrix<T, N, M> result; - for (int i = 0; i < N; ++i) - result[i] = WaveMultiPrefixBitOr(expr[i], mask); - return result; + return spirv_asm + { + OpExtension "SPV_KHR_subgroup_rotate"; + OpCapability GroupNonUniformRotateKHR; + result:$$vector<T,N> = OpGroupNonUniformRotateKHR Subgroup $value $delta $clusterSize; + }; } } -/// @category wave -__generic<T : __BuiltinIntegerType> -__glsl_extension(GL_NV_shader_subgroup_partitioned) -__spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -T WaveMultiPrefixBitXor(T expr, uint4 mask) + +// +// WaveMulti intrinsics are subgroup operations that operate on a 128-bit `uint4` mask. +// They are equivalent to SPIRV/GLSL's subgroup partitioned operation and HLSL's `WaveMultiPrefix*` operations. +// +// SPIRV/GLSL natively supports masked subgroup operations for both reductions and exclusive/inclusive scans. +// HLSL only natively supports exclusive scans(prefix operations) on arithmetic operations. Inclusve scans +// are emulated by performing an additional operation to the inclusive scan result. Reductions are not supported. +// + +__generic<T : __BuiltinType> +[ForceInline] +void __shaderSubgroupPartitionedPreamble() { + shader_subgroup_preamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixXor(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitXor"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveXorNV"; + case glsl: + __requireTargetExtension("GL_NV_shader_subgroup_partitioned"); case spirv: - return spirv_asm + spirv_asm { OpExtension "SPV_NV_shader_subgroup_partitioned"; OpCapability GroupNonUniformPartitionedNV; - result:$$T = OpGroupNonUniformBitwiseXor Subgroup PartitionedExclusiveScanNV $expr $mask }; + default: + return; } } -__generic<T : __BuiltinIntegerType, let N : int> -__glsl_extension(GL_NV_shader_subgroup_partitioned) +// +// WaveMultiSum/WaveMultiProduct. +// +${{{{ +struct WaveMultiSumProductEntry { const char* name; const char* spirvName; }; +const WaveMultiSumProductEntry kWaveMultiSumProductNames[] = { {"Sum", "Add"}, {"Product", "Mul"} }; +for (auto opName : kWaveMultiSumProductNames) { +}}}} + +__generic<T : __BuiltinArithmeticType> __spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -vector<T,N> WaveMultiPrefixBitXor(vector<T,N> expr, uint4 mask) +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +T WaveMulti$(opName.name)(T value, uint4 mask) { + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixXorMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitXor"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveXorNV"; + case cuda: + __intrinsic_asm "_wave$(opName.name)($1.x, $0)"; + case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.spirvName)NV"; case spirv: - return spirv_asm { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - result:$$vector<T,N> = OpGroupNonUniformBitwiseXor Subgroup PartitionedExclusiveScanNV $expr $mask - }; + if (__isFloat<T>()) + return spirv_asm { result:$$T = OpGroupNonUniformF$(opName.spirvName) Subgroup PartitionedReduceNV $value $mask }; + else + return spirv_asm { result:$$T = OpGroupNonUniformI$(opName.spirvName) Subgroup PartitionedReduceNV $value $mask }; + } } } -__generic<T : __BuiltinIntegerType, let N : int, let M : int> -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -matrix<T,N,M> WaveMultiPrefixBitXor(matrix<T,N,M> expr, uint4 mask) +__generic<T : __BuiltinArithmeticType, let N : int> +__spirv_version(1.3) +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +vector<T, N> WaveMulti$(opName.name)(vector<T, N> value, uint4 mask) { + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixXorMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixBitXor"; + case cuda: + __intrinsic_asm "_wave$(opName.name)Multiple($1.x, $0)"; case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.spirvName)NV"; case spirv: + { + if (__isFloat<T>()) + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformF$(opName.spirvName) Subgroup PartitionedReduceNV $value $mask }; + else + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformI$(opName.spirvName) Subgroup PartitionedReduceNV $value $mask }; + } + } +} + +__generic<T : __BuiltinArithmeticType, let N : int, let M : int> +[require(cuda_glsl_spirv, subgroup_partitioned)] +matrix<T,N,M> WaveMulti$(opName.name)(matrix<T,N,M> value, uint4 mask) +{ + __target_switch + { + case cuda: + __intrinsic_asm "_wave$(opName.name)Multiple($1.x, $0)"; + default: matrix<T, N, M> result; for (int i = 0; i < N; ++i) - result[i] = WaveMultiPrefixBitXor(expr[i], mask); + result[i] = WaveMulti$(opName.name)(value[i], mask); return result; } } -/// @category wave +${{{{ +} // WaveMultiSum/WaveMultiProduct. +}}}} + + +// +// WaveMultiPrefixInclusiveSum/WaveMultiPrefixInclusiveProduct. +// WaveMultiPrefixExclusiveSum/WaveMultiPrefixExclusiveProduct. +// WaveMultiPrefixSum/WaveMultiPrefixProduct. +// +${{{{ +struct WaveMultiPrefixSumProductEntry +{ + const char* name; + const char* spirvName; + const char* spirvGroupOperation; + const char* glslName; + const char* hlslName; + const char* cudaName; + const char* cudaExtraOperation; + + // Inclusive operations are not implemented by the CUDA prelude functions. + // They are implemented here by calling the exclusive implementation and performing an additional operations + // with the current invocation's value. This works for all cases except for element-wise matrix multiplication. + bool cudaMatrixVariantSupport; +}; + +const WaveMultiPrefixSumProductEntry kWaveMultiPrefixSumProductNames[] = +{ + // name spirvName spirvGroupOperation glslName hlslName cudaName cudaExtraOperation cudaMatrixVariantSupport + { "InclusiveSum", "Add", "PartitionedInclusiveScanNV", "InclusiveAdd", "Sum($0, $1) + $0", "Sum", "+ $0", false }, + { "InclusiveProduct", "Mul", "PartitionedInclusiveScanNV", "InclusiveMul", "Product($0, $1) * $0", "Product", "* $0", false }, + { "ExclusiveSum", "Add", "PartitionedExclusiveScanNV", "ExclusiveAdd", "Sum($0, $1)", "Sum", "", true }, + { "ExclusiveProduct", "Mul", "PartitionedExclusiveScanNV", "ExclusiveMul", "Product($0, $1)", "Product", "", true }, + + // These are HLSL SM 6.5 intrinsics and are equal to the exclusive variants. + { "Sum", "Add", "PartitionedExclusiveScanNV", "ExclusiveAdd", "Sum($0, $1)", "Sum", "", true }, + { "Product", "Mul", "PartitionedExclusiveScanNV", "ExclusiveMul", "Product($0, $1)", "Product", "", true }, +}; + +for (auto opName : kWaveMultiPrefixSumProductNames) { +}}}} + __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_NV_shader_subgroup_partitioned) __spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -T WaveMultiPrefixProduct(T value, uint4 mask) +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +T WaveMultiPrefix$(opName.name)(T value, uint4 mask) { + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixProduct(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixProduct"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveMulNV"; + case cuda: + __intrinsic_asm "_wavePrefix$(opName.cudaName)($1.x, $0) $(opName.cudaExtraOperation)"; + case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.glslName)NV"; + case hlsl: + __intrinsic_asm "WaveMultiPrefix$(opName.hlslName)"; case spirv: { - spirv_asm - { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - }; - if (__isFloat<T>()) - { - return spirv_asm - { - result:$$T = OpGroupNonUniformFMul Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$T = OpGroupNonUniformF$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; else - { - return spirv_asm - { - result:$$T = OpGroupNonUniformIMul Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$T = OpGroupNonUniformI$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; } } } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_NV_shader_subgroup_partitioned) __spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -vector<T,N> WaveMultiPrefixProduct(vector<T,N> value, uint4 mask) +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +vector<T, N> WaveMultiPrefix$(opName.name)(vector<T, N> value, uint4 mask) { + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixProductMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixProduct"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveMulNV"; + case cuda: + __intrinsic_asm "_wavePrefix$(opName.cudaName)Multiple($1.x, $0) $(opName.cudaExtraOperation)"; + case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.glslName)NV"; + case hlsl: + __intrinsic_asm "WaveMultiPrefix$(opName.hlslName)"; case spirv: { - spirv_asm - { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - }; - if (__isFloat<T>()) - { - return spirv_asm - { - result:$$vector<T,N> = OpGroupNonUniformFMul Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformF$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; else - { - return spirv_asm - { - result:$$vector<T,N> = OpGroupNonUniformIMul Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformI$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; } } } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -matrix<T,N,M> WaveMultiPrefixProduct(matrix<T,N,M> value, uint4 mask) +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +matrix<T,N,M> WaveMultiPrefix$(opName.name)(matrix<T,N,M> value, uint4 mask) { __target_switch { - case cuda: __intrinsic_asm "_wavePrefixProductMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixProduct"; - case glsl: - case spirv: + ${{{{ if(opName.cudaMatrixVariantSupport) { }}}} + case cuda: + __intrinsic_asm "_wavePrefix$(opName.cudaName)Multiple($1.x, $0) $(opName.cudaExtraOperation)"; + ${{{{ } }}}} + default: matrix<T, N, M> result; for (int i = 0; i < N; ++i) - result[i] = WaveMultiPrefixProduct(value[i], mask); + result[i] = WaveMultiPrefix$(opName.name)(value[i], mask); return result; } } -/// @category wave +${{{{ +} +// WaveMultiPrefixInclusiveSum/WaveMultiPrefixInclusiveProduct. +// WaveMultiPrefixExclusiveSum/WaveMultiPrefixExclusiveProduct. +// WaveMultiPrefixSum/WaveMultiPrefixProduct. +}}}} + + +// +// WaveMultiMin/WaveMultiMax. +// +${{{{ +struct WaveMultiMinMaxEntry { const char* name; }; +const WaveMultiMinMaxEntry kWaveMultiMinMaxNames[] = { {"Min"}, {"Max"} }; +for (auto opName : kWaveMultiMinMaxNames) { +}}}} + __generic<T : __BuiltinArithmeticType> -__glsl_extension(GL_NV_shader_subgroup_partitioned) __spirv_version(1.3) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -T WaveMultiPrefixSum(T value, uint4 mask) +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +T WaveMulti$(opName.name)(T value, uint4 mask) { + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixSum(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixSum"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveAddNV"; + case cuda: + __intrinsic_asm "_wave$(opName.name)($1.x, $0)"; + case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.name)NV"; case spirv: { - spirv_asm - { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - }; - if (__isFloat<T>()) - { - return spirv_asm - { - result:$$T = OpGroupNonUniformFAdd Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$T = OpGroupNonUniformF$(opName.name) Subgroup PartitionedReduceNV $value $mask }; + else if (__isUnsignedInt<T>()) + return spirv_asm { result:$$T = OpGroupNonUniformU$(opName.name) Subgroup PartitionedReduceNV $value $mask }; else - { - return spirv_asm - { - result:$$T = OpGroupNonUniformIAdd Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$T = OpGroupNonUniformS$(opName.name) Subgroup PartitionedReduceNV $value $mask }; } } } __generic<T : __BuiltinArithmeticType, let N : int> -__glsl_extension(GL_NV_shader_subgroup_partitioned) -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] __spirv_version(1.3) -vector<T,N> WaveMultiPrefixSum(vector<T,N> value, uint4 mask) +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +vector<T, N> WaveMulti$(opName.name)(vector<T, N> value, uint4 mask) { + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { - case cuda: __intrinsic_asm "_wavePrefixSumMultiple(_getMultiPrefixMask(($1).x), $0 )"; - case hlsl: __intrinsic_asm "WaveMultiPrefixSum"; - case glsl: __intrinsic_asm "subgroupPartitionedExclusiveAddNV"; + case cuda: + __intrinsic_asm "_wave$(opName.name)Multiple($1.x, $0)"; + case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.name)NV"; case spirv: { - spirv_asm - { - OpExtension "SPV_NV_shader_subgroup_partitioned"; - OpCapability GroupNonUniformPartitionedNV; - }; - if (__isFloat<T>()) - { - return spirv_asm - { - result:$$vector<T,N> = OpGroupNonUniformFAdd Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformF$(opName.name) Subgroup PartitionedReduceNV $value $mask }; + else if (__isUnsignedInt<T>()) + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformU$(opName.name) Subgroup PartitionedReduceNV $value $mask }; else - { - return spirv_asm - { - result:$$vector<T,N> = OpGroupNonUniformIAdd Subgroup PartitionedExclusiveScanNV $value $mask - }; - } + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformS$(opName.name) Subgroup PartitionedReduceNV $value $mask }; } } } __generic<T : __BuiltinArithmeticType, let N : int, let M : int> -[require(cuda_glsl_hlsl_spirv, wave_multi_prefix)] -matrix<T,N,M> WaveMultiPrefixSum(matrix<T,N,M> value, uint4 mask) +[require(cuda_glsl_spirv, subgroup_partitioned)] +matrix<T, N, M> WaveMulti$(opName.name)(matrix<T, N, M> value, uint4 mask) { __target_switch { - case cuda: __intrinsic_asm "_wavePrefixSumMultiple(_getMultiPrefixMask(($1).x), $0)"; - case hlsl: __intrinsic_asm "WaveMultiPrefixSum"; - case glsl: - case spirv: + case cuda: + __intrinsic_asm "_wave$(opName.name)Multiple($1.x, $0)"; + default: matrix<T, N, M> result; + [ForceUnroll] for (int i = 0; i < N; ++i) - result[i] = WaveMultiPrefixSum(value[i], mask); + result[i] = WaveMulti$(opName.name)(value[i], mask); return result; } } -__glsl_extension(GL_EXT_demote_to_helper_invocation) -[ForceInline] -[require(glsl_hlsl_metal_spirv, helper_lane)] -bool IsHelperLane() -{ - __target_switch { - case hlsl: __intrinsic_asm "IsHelperLane()"; - case glsl: __intrinsic_asm "gl_HelperInvocation"; - case metal: __intrinsic_asm "simd_is_helper_thread()"; - case spirv: - return spirv_asm { - OpExtension "SPV_EXT_demote_to_helper_invocation"; - OpCapability DemoteToHelperInvocationEXT; - result:$$bool = OpIsHelperInvocationEXT - }; - } -} +${{{{ +} // WaveMultiMin/WaveMultiMax. +}}}} -//@hidden: -__generic<T : __BuiltinType> -[ForceInline] -[require(glsl)] -void __requireGLSLShaderSubgroupTypeExtension() +// +// WaveMultiPrefixInclusiveMin/WaveMultiPrefixInclusiveMax. +// WaveMultiPrefixExclusiveMin/WaveMultiPrefixExclusiveMax. +// +${{{{ +struct WaveMultiPrefixMinMaxEntry { - // the following is a seperate function call, since else the `__requireTargetExtension` and associated __intrinsic_asm is ignored if the calling function also calls an __intrinsic_asm - if (__type_equals<T, half>() - || __type_equals<T, float16_t>() - ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_float16"); - else if (__type_equals<T, uint8_t>() - || __type_equals<T, int8_t>() - ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_int8"); - else if (__type_equals<T, uint16_t>() - || __type_equals<T, int16_t>() - ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_int16"); - else if (__type_equals<T, uint64_t>() - || __type_equals<T, int64_t>() - ) __requireTargetExtension("GL_EXT_shader_subgroup_extended_types_int64"); + const char* name; + const char* spirvName; + const char* spirvGroupOperation; + const char* glslName; +}; - __intrinsic_asm ""; -} +const WaveMultiPrefixMinMaxEntry kWaveMultiPrefixMinMaxNames[] = +{ + // name spirvName spirvGroupOperation glslName + { "InclusiveMin", "Min", "PartitionedInclusiveScanNV", "InclusiveMin" }, + { "InclusiveMax", "Max", "PartitionedInclusiveScanNV", "InclusiveMax" }, + { "ExclusiveMin", "Min", "PartitionedExclusiveScanNV", "ExclusiveMin" }, + { "ExclusiveMax", "Max", "PartitionedExclusiveScanNV", "ExclusiveMax" }, +}; -__generic<T : __BuiltinType> +for (auto opName : kWaveMultiPrefixMinMaxNames) { +}}}} + +__generic<T : __BuiltinArithmeticType> +__spirv_version(1.3) [ForceInline] -[require(metal)] -void __checkMetalShaderSubgroupType() +[require(glsl_spirv, subgroup_partitioned)] +T WaveMultiPrefix$(opName.name)(T value, uint4 mask) { - // These builtin types are not supported for Metal's `simd` operations. - if (__type_equals<T, uint8_t>() - || __type_equals<T, int8_t>() - || __type_equals<T, uint64_t>() - || __type_equals<T, int64_t>() - || __isBool<T>() - ) + __shaderSubgroupPartitionedPreamble<T>(); + __target_switch { - static_assert(false, "Unsupported type for subgroup operations in Metal. Valid types include scalars and vectors of uint/uint32_t, int/int32_t, uint16_t, int16_t, float, and half."); + case glsl: + __intrinsic_asm "subgroupPartitioned$(opName.glslName)NV"; + case spirv: + { + if (__isFloat<T>()) + return spirv_asm { result:$$T = OpGroupNonUniformF$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; + else if (__isUnsignedInt<T>()) + return spirv_asm { result:$$T = OpGroupNonUniformU$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; + else + return spirv_asm { result:$$T = OpGroupNonUniformS$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; + } } } -__generic<T : __BuiltinType> -void shader_subgroup_preamble() +__generic<T : __BuiltinArithmeticType, let N : int> +__spirv_version(1.3) +[ForceInline] +[require(glsl_spirv, subgroup_partitioned)] +vector<T, N> WaveMultiPrefix$(opName.name)(vector<T, N> value, uint4 mask) { - // checks needed for shader_subgroup functions; __requireTargetExtension does not work - // (does not add the ext specified correctly to the compile output; using extended type - // will result in error for using the type) + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { case glsl: - __requireGLSLShaderSubgroupTypeExtension<T>(); - case metal: - __checkMetalShaderSubgroupType<T>(); - default: - return; + __intrinsic_asm "subgroupPartitioned$(opName.glslName)NV"; + case spirv: + { + if (__isFloat<T>()) + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformF$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; + else if (__isUnsignedInt<T>()) + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformU$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; + else + return spirv_asm { result:$$vector<T,N> = OpGroupNonUniformS$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask }; + } } } -//@public: +__generic<T : __BuiltinArithmeticType, let N : int, let M : int> +[require(glsl_spirv, subgroup_partitioned)] +matrix<T, N, M> WaveMultiPrefix$(opName.name)(matrix<T, N, M> value, uint4 mask) +{ + matrix<T, N, M> result; + [ForceUnroll] + for (int i = 0; i < N; ++i) + result[i] = WaveMultiPrefix$(opName.name)(value[i], mask); + return result; +} + +${{{{ +} +// WaveMultiPrefixInclusiveMin/WaveMultiPrefixInclusiveMax. +// WaveMultiPrefixExclusiveMin/WaveMultiPrefixExclusiveMax. +}}}} + // -// Wave Rotate intrinsics. -// These are Slang specific intrinsics to rotate values within a subgroup. +// WaveMultiBitAnd/WaveMultiBitOr/WaveMultiBitXor. // +${{{{ +struct WaveMultiBitsEntry { const char* name; }; +const WaveMultiBitsEntry kWaveMultiBitsNames[] = { {"And"}, {"Or"} , {"Xor"} }; +for (auto opName : kWaveMultiBitsNames) { +}}}} -__generic<T : __BuiltinType> -__glsl_extension(GL_KHR_shader_subgroup_rotate) -[require(glsl_metal_spirv, subgroup_rotate)] -T WaveRotate(T value, uint delta) +__generic<T : __BuiltinLogicalType> +__spirv_version(1.3) +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +T WaveMultiBit$(opName.name)(T value, uint4 mask) { - shader_subgroup_preamble<T>(); + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { + case cuda: + __intrinsic_asm "_wave$(opName.name)($1.x, $0)"; case glsl: - __intrinsic_asm "subgroupRotate"; - case metal: - __intrinsic_asm "simd_shuffle_rotate_down"; + __intrinsic_asm "subgroupPartitioned$(opName.name)NV"; case spirv: return spirv_asm { - OpExtension "SPV_KHR_subgroup_rotate"; - OpCapability GroupNonUniformRotateKHR; - result:$$T = OpGroupNonUniformRotateKHR Subgroup $value $delta; + result:$$T = OpGroupNonUniformBitwise$(opName.name) Subgroup PartitionedReduceNV $value $mask; }; } } -__generic<T : __BuiltinType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_rotate) -[require(glsl_metal_spirv, subgroup_rotate)] -vector<T, N> WaveRotate(vector<T, N> value, uint delta) +__generic<T : __BuiltinLogicalType, let N : int> +__spirv_version(1.3) +[ForceInline] +[require(cuda_glsl_spirv, subgroup_partitioned)] +vector<T, N> WaveMultiBit$(opName.name)(vector<T, N> value, uint4 mask) { - shader_subgroup_preamble<T>(); + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { + case cuda: + __intrinsic_asm "_wave$(opName.name)Multiple($1.x, $0)"; case glsl: - __intrinsic_asm "subgroupRotate"; - case metal: - __intrinsic_asm "simd_shuffle_rotate_down"; + __intrinsic_asm "subgroupPartitioned$(opName.name)NV"; case spirv: return spirv_asm { - OpExtension "SPV_KHR_subgroup_rotate"; - OpCapability GroupNonUniformRotateKHR; - result:$$vector<T,N> = OpGroupNonUniformRotateKHR Subgroup $value $delta; + result:$$vector<T,N> = OpGroupNonUniformBitwise$(opName.name) Subgroup PartitionedReduceNV $value $mask; }; } } -__generic<T : __BuiltinType> -__glsl_extension(GL_KHR_shader_subgroup_rotate) -[require(glsl_spirv, subgroup_rotate)] -T WaveClusteredRotate(T value, uint delta, constexpr uint clusterSize) +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[require(cuda_glsl_spirv, subgroup_partitioned)] +matrix<T, N, M> WaveMultiBit$(opName.name)(matrix<T, N, M> value, uint4 mask) { - shader_subgroup_preamble<T>(); __target_switch { + case cuda: + __intrinsic_asm "_wave$(opName.name)Multiple($1.x, $0)"; + default: + matrix<T,N,M> result; + [ForceUnroll] + for (int i = 0; i < N; ++i) + result[i] = WaveMultiBit$(opName.name)(value[i], mask); + return result; + } +} + +${{{{ +} // WaveMultiBitAnd/WaveMultiBitOr/WaveMultiBitXor. +}}}} + + +// +// WaveMultiPrefixInclusiveBitAnd/WaveMultiPrefixInclusiveBitOr/WaveMultiInclusiveBitXor. +// WaveMultiPrefixExclusiveBitAnd/WaveMultiPrefixExclusiveBitXor/WaveMultiExclusiveBitXor. +// WaveMultiPrefixBitAnd/WaveMultiPrefixBitOr/WaveMultiBitXor. +// +${{{{ +struct WaveMultiPrefixBitwiseEntry +{ + const char* name; + const char* spirvName; + const char* spirvGroupOperation; + const char* glslName; + const char* hlslName; + const char* cudaExtraOperation; + + bool cudaMatrixVariantSupport; +}; + +const WaveMultiPrefixBitwiseEntry kWaveMultiPrefixBitwiseNames[] = +{ + // name spirvName spirvGroupOperation glslName hlslName cudaExtraOperation cudaMatrixVariantSupport + { "InclusiveBitAnd", "And", "PartitionedInclusiveScanNV", "InclusiveAnd", "And($0, $1) & $0", "& $0", false }, + { "InclusiveBitOr", "Or", "PartitionedInclusiveScanNV", "InclusiveOr", "Or($0, $1) | $0", "| $0", false }, + { "InclusiveBitXor", "Xor", "PartitionedInclusiveScanNV", "InclusiveXor", "Xor($0, $1) ^ $0", "^ $0", false }, + { "ExclusiveBitAnd", "And", "PartitionedExclusiveScanNV", "ExclusiveAnd", "And", "", true }, + { "ExclusiveBitOr", "Or", "PartitionedExclusiveScanNV", "ExclusiveOr", "Or", "", true }, + { "ExclusiveBitXor", "Xor", "PartitionedExclusiveScanNV", "ExclusiveXor", "Xor", "", true }, + + // These are HLSL SM 6.5 intrinsics and are equal to the exclusive variants. + { "BitAnd", "And", "PartitionedExclusiveScanNV", "ExclusiveAnd", "And", "", true }, + { "BitOr", "Or", "PartitionedExclusiveScanNV", "ExclusiveOr", "Or", "", true }, + { "BitXor", "Xor", "PartitionedExclusiveScanNV", "ExclusiveXor", "Xor", "", true }, +}; + +for (auto opName : kWaveMultiPrefixBitwiseNames) { +}}}} + +__generic<T : __BuiltinLogicalType> +__spirv_version(1.3) +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +T WaveMultiPrefix$(opName.name)(T value, uint4 mask) +{ + __shaderSubgroupPartitionedPreamble<T>(); + __target_switch + { + case cuda: + __intrinsic_asm "_wavePrefix$(opName.spirvName)($1.x, $0) $(opName.cudaExtraOperation)"; case glsl: - __intrinsic_asm "subgroupClusteredRotate"; + __intrinsic_asm "subgroupPartitioned$(opName.glslName)NV"; + case hlsl: + __intrinsic_asm "WaveMultiPrefixBit$(opName.hlslName)"; case spirv: return spirv_asm { - OpExtension "SPV_KHR_subgroup_rotate"; - OpCapability GroupNonUniformRotateKHR; - result:$$T = OpGroupNonUniformRotateKHR Subgroup $value $delta $clusterSize; + result:$$T = OpGroupNonUniformBitwise$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask; }; } } -__generic<T : __BuiltinType, let N : int> -__glsl_extension(GL_KHR_shader_subgroup_rotate) -[require(glsl_spirv, subgroup_rotate)] -vector<T, N> WaveClusteredRotate(vector<T, N> value, uint delta, constexpr uint clusterSize) +__generic<T : __BuiltinLogicalType, let N : int> +__spirv_version(1.3) +[ForceInline] +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +vector<T, N> WaveMultiPrefix$(opName.name)(vector<T, N> value, uint4 mask) { - shader_subgroup_preamble<T>(); + __shaderSubgroupPartitionedPreamble<T>(); __target_switch { + case cuda: + __intrinsic_asm "_wavePrefix$(opName.spirvName)Multiple($1.x, $0) $(opName.cudaExtraOperation)"; case glsl: - __intrinsic_asm "subgroupClusteredRotate"; + __intrinsic_asm "subgroupPartitioned$(opName.glslName)NV"; + case hlsl: + __intrinsic_asm "WaveMultiPrefixBit$(opName.hlslName)"; case spirv: return spirv_asm { - OpExtension "SPV_KHR_subgroup_rotate"; - OpCapability GroupNonUniformRotateKHR; - result:$$vector<T,N> = OpGroupNonUniformRotateKHR Subgroup $value $delta $clusterSize; + result:$$vector<T,N> = OpGroupNonUniformBitwise$(opName.spirvName) Subgroup $(opName.spirvGroupOperation) $value $mask; }; } } +__generic<T : __BuiltinLogicalType, let N : int, let M : int> +[require(cuda_glsl_hlsl_spirv, subgroup_partitioned)] +matrix<T, N, M> WaveMultiPrefix$(opName.name)(matrix<T, N, M> value, uint4 mask) +{ + __target_switch + { +${{{{ + if (opName.cudaMatrixVariantSupport) { +}}}} + case cuda: + __intrinsic_asm "_wavePrefix$(opName.spirvName)Multiple($1.x, $0) $(opName.cudaExtraOperation)"; +${{{{ + } +}}}} + default: + matrix<T,N,M> result; + [ForceUnroll] + for (int i = 0; i < N; ++i) + result[i] = WaveMultiPrefix$(opName.name)(value[i], mask); + return result; + } +} +${{{{ +} +// WaveMultiPrefixInclusiveBitAnd/WaveMultiPrefixInclusiveBitOr/WaveMultiInclusiveBitXor. +// WaveMultiPrefixExclusiveBitAnd/WaveMultiPrefixExclusiveBitXor/WaveMultiExclusiveBitXor. +// WaveMultiPrefixBitAnd/WaveMultiPrefixBitOr/WaveMultiBitXor. +}}}} + + // // Quad Control intrinsics // diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef index 1799d4bfc..48617c54d 100644 --- a/source/slang/slang-capabilities.capdef +++ b/source/slang/slang-capabilities.capdef @@ -1157,11 +1157,6 @@ alias fragmentshaderbarycentric = GL_EXT_fragment_shader_barycentric | _sm_6_1; /// (gfx targets) Capabilities needed to use memory barriers /// [Compound] alias shadermemorycontrol = glsl | _spirv_1_0 | _sm_5_0; -/// Capabilities needed to use HLSL tier wave operations -/// [Compound] -alias wave_multi_prefix = _sm_6_5 - | _cuda_sm_7_0 - | GL_KHR_shader_subgroup_ballot + GL_KHR_shader_subgroup_arithmetic + GL_NV_shader_subgroup_partitioned; /// Capabilities needed to use GLSL buffer-reference's /// [Compound] alias bufferreference = GL_EXT_buffer_reference; @@ -2186,7 +2181,9 @@ alias subgroup_quad = GL_KHR_shader_subgroup_quad ; /// Capabilities required to use GLSL-style subgroup operations 'subgroup_partitioned' /// [Compound] -alias subgroup_partitioned = GL_NV_shader_subgroup_partitioned + subgroup_ballot_activemask | _sm_6_5 | _cuda_sm_7_0; +alias subgroup_partitioned = _sm_6_5 + | _cuda_sm_7_0 + | GL_KHR_shader_subgroup_ballot + GL_KHR_shader_subgroup_arithmetic + GL_NV_shader_subgroup_partitioned; /// Capabilities required to use GLSL-style subgroup rotate operations 'subgroup_rotate' diff --git a/tests/hlsl-intrinsic/wave-mask/wave-active-product.slang b/tests/hlsl-intrinsic/wave-mask/wave-active-product.slang index 8a47c5733..da94ad794 100644 --- a/tests/hlsl-intrinsic/wave-mask/wave-active-product.slang +++ b/tests/hlsl-intrinsic/wave-mask/wave-active-product.slang @@ -1,7 +1,7 @@ //TEST_CATEGORY(wave-mask, compute) //DISABLE_TEST:COMPARE_COMPUTE_EX:-cpu -compute -shaderobj //DISABLE_TEST:COMPARE_COMPUTE_EX:-slang -compute -shaderobj -//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_0 -shaderobj +//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_5 -shaderobj //TEST(vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj //TEST:COMPARE_COMPUTE_EX:-cuda -compute -shaderobj @@ -26,4 +26,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) const WaveMask mask2 = mask0 & ~mask1; outputBuffer[idx] = WaveMaskProduct(mask2, idx); -}
\ No newline at end of file +} diff --git a/tests/hlsl-intrinsic/wave-mask/wave-diverge.slang b/tests/hlsl-intrinsic/wave-mask/wave-diverge.slang index 3dd33f150..3a1c26f8e 100644 --- a/tests/hlsl-intrinsic/wave-mask/wave-diverge.slang +++ b/tests/hlsl-intrinsic/wave-mask/wave-diverge.slang @@ -1,7 +1,7 @@ //TEST_CATEGORY(wave-mask, compute) //DISABLE_TEST:COMPARE_COMPUTE_EX:-cpu -compute -shaderobj //DISABLE_TEST:COMPARE_COMPUTE_EX:-slang -compute -shaderobj -//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_0 -shaderobj +//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_5 -shaderobj //TEST(vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj //TEST:COMPARE_COMPUTE_EX:-cuda -compute -shaderobj @@ -30,4 +30,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) value = WaveMaskMin(mask2, idx + 1); outputBuffer[idx] = value; -}
\ No newline at end of file +} diff --git a/tests/hlsl-intrinsic/wave-mask/wave-matrix.slang b/tests/hlsl-intrinsic/wave-mask/wave-matrix.slang index f333a59fb..fb5573bd1 100644 --- a/tests/hlsl-intrinsic/wave-mask/wave-matrix.slang +++ b/tests/hlsl-intrinsic/wave-mask/wave-matrix.slang @@ -1,7 +1,7 @@ //TEST_CATEGORY(wave-mask, compute) //DISABLE_TEST:COMPARE_COMPUTE_EX:-cpu -compute -shaderobj //DISABLE_TEST:COMPARE_COMPUTE_EX:-slang -compute -shaderobj -//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_0 -shaderobj -render-feature hardware-device +//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_5 -shaderobj -render-feature hardware-device //DISABLE_TEST(vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj //TEST:COMPARE_COMPUTE_EX:-cuda -compute -shaderobj @@ -37,4 +37,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) matrix<int, 2, 2> r = r0 + matrix<int, 2, 2>(r1) + r6; outputBuffer[idx] = r[0][0] + r[0][1] + r[1][0] + r[1][1]; -}
\ No newline at end of file +} diff --git a/tests/hlsl-intrinsic/wave-mask/wave-prefix-product.slang b/tests/hlsl-intrinsic/wave-mask/wave-prefix-product.slang index b12e9c1b3..e32524b1e 100644 --- a/tests/hlsl-intrinsic/wave-mask/wave-prefix-product.slang +++ b/tests/hlsl-intrinsic/wave-mask/wave-prefix-product.slang @@ -1,7 +1,7 @@ //TEST_CATEGORY(wave-mask, compute) //DISABLE_TEST:COMPARE_COMPUTE_EX:-cpu -compute -shaderobj //DISABLE_TEST:COMPARE_COMPUTE_EX:-slang -compute -shaderobj -//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_0 -shaderobj -render-feature hardware-device +//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_5 -shaderobj -render-feature hardware-device //TEST(vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -render-feature hardware-device //TEST:COMPARE_COMPUTE_EX:-cuda -compute -shaderobj @@ -25,4 +25,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) outputBuffer[idx] = r0 + (r2 << 16); -}
\ No newline at end of file +} diff --git a/tests/hlsl-intrinsic/wave-mask/wave-prefix-sum.slang b/tests/hlsl-intrinsic/wave-mask/wave-prefix-sum.slang index 51e9b7600..2e0fba746 100644 --- a/tests/hlsl-intrinsic/wave-mask/wave-prefix-sum.slang +++ b/tests/hlsl-intrinsic/wave-mask/wave-prefix-sum.slang @@ -1,7 +1,7 @@ //TEST_CATEGORY(wave-mask, compute) //DISABLE_TEST:COMPARE_COMPUTE_EX:-cpu -compute -shaderobj //DISABLE_TEST:COMPARE_COMPUTE_EX:-slang -compute -shaderobj -//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_0 -shaderobj -render-feature hardware-device +//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_5 -shaderobj -render-feature hardware-device //TEST(vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -render-feature hardware-device //TEST:COMPARE_COMPUTE_EX:-cuda -compute -shaderobj @@ -23,4 +23,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) int r2 = int(r1.x) + int(r1.y) - idx; outputBuffer[idx] = r0 + (r2 << 16); -}
\ No newline at end of file +} diff --git a/tests/hlsl-intrinsic/wave-mask/wave-vector.slang b/tests/hlsl-intrinsic/wave-mask/wave-vector.slang index b1f44f4fb..7c326e0f3 100644 --- a/tests/hlsl-intrinsic/wave-mask/wave-vector.slang +++ b/tests/hlsl-intrinsic/wave-mask/wave-vector.slang @@ -1,7 +1,7 @@ //TEST_CATEGORY(wave-mask, compute) //DISABLE_TEST:COMPARE_COMPUTE_EX:-cpu -compute -shaderobj //DISABLE_TEST:COMPARE_COMPUTE_EX:-slang -compute -shaderobj -//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_0 -shaderobj -render-feature hardware-device +//TEST:COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -profile cs_6_5 -shaderobj -render-feature hardware-device //TEST(vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -render-feature hardware-device //TEST:COMPARE_COMPUTE_EX:-cuda -compute -shaderobj @@ -29,4 +29,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) int2 r = r0 + int2(r1) + r2 + r3 + r4; outputBuffer[idx] = r.x + r.y; -}
\ No newline at end of file +} diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-bitwise.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-bitwise.slang new file mode 100644 index 000000000..c2a292c14 --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-bitwise.slang @@ -0,0 +1,139 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-cuda -compute -shaderobj -xslang -DCUDA + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedAnd subgroupPartitionedAndNV +#define __partitionedOr subgroupPartitionedOrNV +#define __partitionedXor subgroupPartitionedXorNV +#else +#define __partitionedAnd WaveMultiBitAnd +#define __partitionedOr WaveMultiBitOr +#define __partitionedXor WaveMultiBitXor +#endif + +static uint gAndValue = 0; +static uint gOrValue = 0; +static uint gOrResult = 0; +static uint gXorValue = 0; +static uint gXorResult = 0; + +__generic<T : __BuiltinLogicalType> +bool test1Bitwise(uint4 mask) +{ + let andValue = T(gAndValue); + let orValue = T(gOrValue); + let orResult = T(gOrResult); + let xorValue = T(gXorValue); + let xorResult = T(gXorResult); + + return true + & (__partitionedAnd(andValue, mask) == andValue) + & (__partitionedOr(orValue, mask) == orResult) + & (__partitionedXor(xorValue, mask) == xorResult) + ; +} + +__generic<T : __BuiltinLogicalType, let N : int> +bool testVBitwise(uint4 mask) { + typealias GVec = vector<T, N>; + + let andValue = GVec(T(gAndValue)); + let orValue = GVec(T(gOrValue)); + let orResult = GVec(T(gOrResult)); + let xorValue = GVec(T(gXorValue)); + let xorResult = GVec(T(gXorResult)); + + return true + & all(__partitionedAnd(andValue, mask) == andValue) + & all(__partitionedOr(orValue, mask) == orResult) + & all(__partitionedXor(xorValue, mask) == xorResult) + ; +} + +bool testBitwise(uint4 mask) +{ + return true + & test1Bitwise<int>(mask) + & testVBitwise<int, 2>(mask) + & testVBitwise<int, 3>(mask) + & testVBitwise<int, 4>(mask) + & test1Bitwise<uint>(mask) + & testVBitwise<uint, 2>(mask) + & testVBitwise<uint, 3>(mask) + & testVBitwise<uint, 4>(mask) + + // TODO: these are failing SPIRV validation and should be fixed. + // SPIRV's ops do not directly accept/return bool. + // & test1Bitwise<bool>(mask) + // & testVBitwise<bool, 2>(mask) + // & testVBitwise<bool, 3>(mask) + // & testVBitwise<bool, 4>(mask) + +#if !defined(CUDA) + & test1Bitwise<int8_t>(mask) + & testVBitwise<int8_t, 2>(mask) + & testVBitwise<int8_t, 3>(mask) + & testVBitwise<int8_t, 4>(mask) + & test1Bitwise<int16_t>(mask) + & testVBitwise<int16_t, 2>(mask) + & testVBitwise<int16_t, 3>(mask) + & testVBitwise<int16_t, 4>(mask) + & test1Bitwise<int64_t>(mask) + & testVBitwise<int64_t, 2>(mask) + & testVBitwise<int64_t, 3>(mask) + & testVBitwise<int64_t, 4>(mask) + & test1Bitwise<uint8_t>(mask) + & testVBitwise<uint8_t, 2>(mask) + & testVBitwise<uint8_t, 3>(mask) + & testVBitwise<uint8_t, 4>(mask) + & test1Bitwise<uint16_t>(mask) + & testVBitwise<uint16_t, 2>(mask) + & testVBitwise<uint16_t, 3>(mask) + & testVBitwise<uint16_t, 4>(mask) + & test1Bitwise<uint64_t>(mask) + & testVBitwise<uint64_t, 2>(mask) + & testVBitwise<uint64_t, 3>(mask) + & testVBitwise<uint64_t, 4>(mask) +#endif + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + let index = dispatchThreadID.x; + + let isSecondGroup = index >= 15; + let mask = isSecondGroup ? uint4(0xFFFF8000, 0, 0, 0) : uint4(0x0007FFF, 0, 0, 0); + + // One invocation in second group is different from others to test or and xor operations. + let isOrSet = (index == 15); + + gAndValue = isSecondGroup ? uint(1) : uint(0); + gOrValue = isOrSet ? uint(1) : uint(0); + gOrResult = isSecondGroup ? uint(1) : uint(0); + + // Alternate 0s and 1s for xor. + gXorValue = (index % 2 == 0) ? uint(0) : uint(1); + if (isOrSet) + { + // This is in second group - disrupt the alternating sequence. + gXorValue = uint(0); + } + gXorResult = isSecondGroup ? uint(0) : uint(1); + + bool result = true + & testBitwise(mask) + ; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-min-max.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-min-max.slang new file mode 100644 index 000000000..419ffecc5 --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-min-max.slang @@ -0,0 +1,127 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-cuda -compute -shaderobj -xslang -DCUDA + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedMin subgroupPartitionedMinNV +#define __partitionedMax subgroupPartitionedMaxNV +#else +#define __partitionedMin WaveMultiMin +#define __partitionedMax WaveMultiMax +#endif + + +static uint gMinResult = 0; +static uint gMaxResult = 0; +static uint gMinMaxValue = 0; + +__generic<T : __BuiltinArithmeticType> +bool test1MinMax(uint4 mask) +{ + let minResult = T(gMinResult); + let maxResult = T(gMaxResult); + let minMaxValue = T(gMinMaxValue); + + return true + & all(__partitionedMin(minMaxValue, mask) == minResult) + & all(__partitionedMax(minMaxValue, mask) == maxResult) + ; +} + +__generic<T : __BuiltinArithmeticType, let N : int> +bool testVMinMax(uint4 mask) { + typealias GVec = vector<T, N>; + + let minResult = GVec(T(gMinResult)); + let maxResult = GVec(T(gMaxResult)); + let minMaxValue = GVec(T(gMinMaxValue)); + + return true + & all(__partitionedMin(minMaxValue, mask) == minResult) + & all(__partitionedMax(minMaxValue, mask) == maxResult) + ; +} + +bool testMinMax(uint4 mask) +{ + return true + & test1MinMax<int>(mask) + & testVMinMax<int, 2>(mask) + & testVMinMax<int, 3>(mask) + & testVMinMax<int, 4>(mask) + & test1MinMax<uint>(mask) + & testVMinMax<uint, 2>(mask) + & testVMinMax<uint, 3>(mask) + & testVMinMax<uint, 4>(mask) + & test1MinMax<float>(mask) + & testVMinMax<float, 2>(mask) + & testVMinMax<float, 3>(mask) + & testVMinMax<float, 4>(mask) + & test1MinMax<double>(mask) + & testVMinMax<double, 2>(mask) + & testVMinMax<double, 3>(mask) + & testVMinMax<double, 4>(mask) + +#if !defined(CUDA) + & test1MinMax<int8_t>(mask) + & testVMinMax<int8_t, 2>(mask) + & testVMinMax<int8_t, 3>(mask) + & testVMinMax<int8_t, 4>(mask) + & test1MinMax<int16_t>(mask) + & testVMinMax<int16_t, 2>(mask) + & testVMinMax<int16_t, 3>(mask) + & testVMinMax<int16_t, 4>(mask) + & test1MinMax<int64_t>(mask) + & testVMinMax<int64_t, 2>(mask) + & testVMinMax<int64_t, 3>(mask) + & testVMinMax<int64_t, 4>(mask) + & test1MinMax<uint8_t>(mask) + & testVMinMax<uint8_t, 2>(mask) + & testVMinMax<uint8_t, 3>(mask) + & testVMinMax<uint8_t, 4>(mask) + & test1MinMax<uint16_t>(mask) + & testVMinMax<uint16_t, 2>(mask) + & testVMinMax<uint16_t, 3>(mask) + & testVMinMax<uint16_t, 4>(mask) + & test1MinMax<uint64_t>(mask) + & testVMinMax<uint64_t, 2>(mask) + & testVMinMax<uint64_t, 3>(mask) + & testVMinMax<uint64_t, 4>(mask) + & test1MinMax<half>(mask) + & testVMinMax<half, 2>(mask) + & testVMinMax<half, 3>(mask) + & testVMinMax<half, 4>(mask) +#endif + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint index = dispatchThreadID.x; + + // Split into two groups, first group has 15 invocations/lanes and second group has 17. + let isSecondGroup = index >= 15; + uint4 mask = isSecondGroup ? uint4(0xFFFF8000, 0, 0, 0) : uint4(0x0007FFF, 0, 0, 0); + + // Set min value on one invocation on each partition/mask. + let isMinInvocation = (index == 0) || (index == 15); + + gMinResult = isSecondGroup ? uint(2) : uint(0); + gMaxResult = isSecondGroup ? uint(3) : uint(1); + gMinMaxValue = isMinInvocation ? gMinResult : gMaxResult; + + bool result = true + && testMinMax(mask) + ; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-bitwise.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-bitwise.slang new file mode 100644 index 000000000..bb1182e5e --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-bitwise.slang @@ -0,0 +1,163 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-slang -compute -dx12 -use-dxil -profile sm_6_5 -shaderobj +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-cuda -compute -shaderobj -xslang -DCUDA + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedInclusiveAnd subgroupPartitionedInclusiveAndNV +#define __partitionedInclusiveOr subgroupPartitionedInclusiveOrNV +#define __partitionedInclusiveXor subgroupPartitionedInclusiveXorNV +#define __partitionedExclusiveAnd subgroupPartitionedExclusiveAndNV +#define __partitionedExclusiveOr subgroupPartitionedExclusiveOrNV +#define __partitionedExclusiveXor subgroupPartitionedExclusiveXorNV +#else +#define __partitionedInclusiveAnd WaveMultiPrefixInclusiveBitAnd +#define __partitionedInclusiveOr WaveMultiPrefixInclusiveBitOr +#define __partitionedInclusiveXor WaveMultiPrefixInclusiveBitXor +#define __partitionedExclusiveAnd WaveMultiPrefixExclusiveBitAnd +#define __partitionedExclusiveOr WaveMultiPrefixExclusiveBitOr +#define __partitionedExclusiveXor WaveMultiPrefixExclusiveBitXor +#endif + + +static uint gAndValue = 0; +static uint gAndResultExclusive = 0; +static uint gOrValue = 0; +static uint gOrResult = 0; +static uint gXorValue = 0; +static uint gXorResultInclusive = 0; +static uint gXorResultExclusive = 0; + +__generic<T : __BuiltinLogicalType> +bool test1Bitwise(uint4 mask) +{ + let andValue = T(gAndValue); + let orValue = T(gOrValue); + let xorValue = T(gXorValue); + + return true + & (__partitionedInclusiveAnd(andValue, mask) == andValue) + & (__partitionedExclusiveAnd(andValue, mask) == T(gAndResultExclusive)) + & (__partitionedInclusiveOr(orValue, mask) == orValue) + & (__partitionedExclusiveOr(orValue, mask) == T(0)) + & (__partitionedInclusiveXor(xorValue, mask) == T(gXorResultInclusive)) + & (__partitionedExclusiveXor(xorValue, mask) == T(gXorResultExclusive)) + ; +} + +__generic<T : __BuiltinLogicalType, let N : int> +bool testVBitwise(uint4 mask) { + typealias GVec = vector<T, N>; + + let andValue = GVec(T(gAndValue)); + let orValue = GVec(T(gOrValue)); + let xorValue = GVec(T(gXorValue)); + + return true + & all(__partitionedInclusiveAnd(andValue, mask) == andValue) + & all(__partitionedExclusiveAnd(andValue, mask) == GVec(T(gAndResultExclusive))) + & all(__partitionedInclusiveOr(orValue, mask) == orValue) + & all(__partitionedExclusiveOr(orValue, mask) == GVec(T(0))) + & all(__partitionedInclusiveXor(xorValue, mask) == GVec(T(gXorResultInclusive))) + & all(__partitionedExclusiveXor(xorValue, mask) == GVec(T(gXorResultExclusive))) + ; +} + +bool testBitwise(uint4 mask) +{ + return true + & test1Bitwise<int>(mask) + & testVBitwise<int, 2>(mask) + & testVBitwise<int, 3>(mask) + & testVBitwise<int, 4>(mask) + & test1Bitwise<uint>(mask) + & testVBitwise<uint, 2>(mask) + & testVBitwise<uint, 3>(mask) + & testVBitwise<uint, 4>(mask) + + // TODO: these are failing SPIRV validation and should be fixed. + // SPIRV's ops do not directly accept/return bool. + // & test1Bitwise<bool>(mask) + // & testVBitwise<bool, 2>(mask) + // & testVBitwise<bool, 3>(mask) + // & testVBitwise<bool, 4>(mask) + +#if defined(VK) + & test1Bitwise<int8_t>(mask) + & testVBitwise<int8_t, 2>(mask) + & testVBitwise<int8_t, 3>(mask) + & testVBitwise<int8_t, 4>(mask) + & test1Bitwise<uint8_t>(mask) + & testVBitwise<uint8_t, 2>(mask) + & testVBitwise<uint8_t, 3>(mask) + & testVBitwise<uint8_t, 4>(mask) +#endif + +#if !defined(CUDA) + & test1Bitwise<int16_t>(mask) + & testVBitwise<int16_t, 2>(mask) + & testVBitwise<int16_t, 3>(mask) + & testVBitwise<int16_t, 4>(mask) + & test1Bitwise<int64_t>(mask) + & testVBitwise<int64_t, 2>(mask) + & testVBitwise<int64_t, 3>(mask) + & testVBitwise<int64_t, 4>(mask) + & test1Bitwise<uint16_t>(mask) + & testVBitwise<uint16_t, 2>(mask) + & testVBitwise<uint16_t, 3>(mask) + & testVBitwise<uint16_t, 4>(mask) + & test1Bitwise<uint64_t>(mask) + & testVBitwise<uint64_t, 2>(mask) + & testVBitwise<uint64_t, 3>(mask) + & testVBitwise<uint64_t, 4>(mask) +#endif + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + let index = dispatchThreadID.x; + + let isSecondGroup = index >= 15; + let mask = isSecondGroup ? uint4(0xFFFF8000, 0, 0, 0) : uint4(0x0007FFF, 0, 0, 0); + let isLastInvocation = (index == 31); + let isLastInPartition = (index == 14) || (index == 31); + let isFirstInPartition = (index == 0) || (index == 15); + + // + // Prefix and. + // - Both groups use 1 except for the last invocation in each partition where input is 0. + // - For inclusive ops, result is 1 except for last invocation in each partition. + // - For exclusive ops, first in partition is always results to ~0(identity). Otherwise exclusive ops result to 1. + gAndValue = isLastInPartition ? uint(0) : uint(1); + gAndResultExclusive = isFirstInPartition ? uint(~0) : uint(1); + + // + // Prefix or. + // - Both groups use 0 except for the last invocation in each partition where input is 1. + // - For inclusive ops, result is 0 except for last invocation in each partition. + // - For exclusive ops, result is always 0. + gOrValue = isLastInPartition ? uint(1) : uint(0); + + // Prefix xor. + // - First group input is always 1. Inclusive results alternate between 1 and 0, starting at 1. Exclusive result is also alternates but starts at 0 (opposite of inclusive result). + // - Second group is always 0. Results are all 0. + gXorValue = isSecondGroup ? uint(0) : uint(1); + gXorResultInclusive = (isSecondGroup || (index % 2 != 0)) ? uint(0) : uint(1); + gXorResultExclusive = isSecondGroup ? uint(0) : (uint(1) - gXorResultInclusive); + + bool result = true + & testBitwise(mask) + ; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-max.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-max.slang new file mode 100644 index 000000000..654fd6130 --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-max.slang @@ -0,0 +1,144 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedInclusiveMax subgroupPartitionedInclusiveMaxNV +#define __partitionedExclusiveMax subgroupPartitionedExclusiveMaxNV +#else +#define __partitionedInclusiveMax WaveMultiPrefixInclusiveMax +#define __partitionedExclusiveMax WaveMultiPrefixExclusiveMax +#endif + +static bool isFirstInPartition = false; +static uint gSmaller = 0; +static uint gLarger = 0; +static uint gMaxValue = 0; + +__generic<T : __BuiltinArithmeticType> +bool test1MinMax(uint4 mask) +{ + let smaller = T(gSmaller); + let maxValue = T(gMaxValue); + + // The larger values are set to be the last in the partition, exclusive variants will never get these values. + bool exclusiveRes = true + & (__partitionedExclusiveMax(maxValue, mask) == smaller) + ; + // Do not check exclusive prefix for the first invocation in partition as their values(identity values) depend on the builtin type `T`. It would be + // nice to have something like T::min or T::max. + if (isFirstInPartition) + { + exclusiveRes = true; + } + + return true + & (__partitionedInclusiveMax(maxValue, mask) == maxValue) + & exclusiveRes + ; +} + +__generic<T : __BuiltinArithmeticType, let N : int> +bool testVMinMax(uint4 mask) { + typealias GVec = vector<T, N>; + + let smaller = GVec(T(gSmaller)); + let maxValue = GVec(T(gMaxValue)); + + // The larger values are set to be the last in the partition, exclusive variants will never get these values. + bool exclusiveRes = true + & all(__partitionedExclusiveMax(maxValue, mask) == smaller) + ; + // Do not check exclusive prefix for the first invocation in partition as their values(identity values) depend on the builtin type `T`. It would be + // nice to have something like T::min or T::max. + if (isFirstInPartition) + { + exclusiveRes = true; + } + + return true + & all(__partitionedInclusiveMax(maxValue, mask) == maxValue) + & exclusiveRes; + ; +} + +bool testMinMax(uint4 mask) +{ + return true + & test1MinMax<int>(mask) + & testVMinMax<int, 2>(mask) + & testVMinMax<int, 3>(mask) + & testVMinMax<int, 4>(mask) + & test1MinMax<uint>(mask) + & testVMinMax<uint, 2>(mask) + & testVMinMax<uint, 3>(mask) + & testVMinMax<uint, 4>(mask) + & test1MinMax<float>(mask) + & testVMinMax<float, 2>(mask) + & testVMinMax<float, 3>(mask) + & testVMinMax<float, 4>(mask) + & test1MinMax<double>(mask) + & testVMinMax<double, 2>(mask) + & testVMinMax<double, 3>(mask) + & testVMinMax<double, 4>(mask) + & test1MinMax<int8_t>(mask) + & testVMinMax<int8_t, 2>(mask) + & testVMinMax<int8_t, 3>(mask) + & testVMinMax<int8_t, 4>(mask) + & test1MinMax<int16_t>(mask) + & testVMinMax<int16_t, 2>(mask) + & testVMinMax<int16_t, 3>(mask) + & testVMinMax<int16_t, 4>(mask) + & test1MinMax<int64_t>(mask) + & testVMinMax<int64_t, 2>(mask) + & testVMinMax<int64_t, 3>(mask) + & testVMinMax<int64_t, 4>(mask) + & test1MinMax<uint8_t>(mask) + & testVMinMax<uint8_t, 2>(mask) + & testVMinMax<uint8_t, 3>(mask) + & testVMinMax<uint8_t, 4>(mask) + & test1MinMax<uint16_t>(mask) + & testVMinMax<uint16_t, 2>(mask) + & testVMinMax<uint16_t, 3>(mask) + & testVMinMax<uint16_t, 4>(mask) + & test1MinMax<uint64_t>(mask) + & testVMinMax<uint64_t, 2>(mask) + & testVMinMax<uint64_t, 3>(mask) + & testVMinMax<uint64_t, 4>(mask) + & test1MinMax<half>(mask) + & testVMinMax<half, 2>(mask) + & testVMinMax<half, 3>(mask) + & testVMinMax<half, 4>(mask) + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +[MaximallyReconverges] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + let index = dispatchThreadID.x; + + // Split into two groups, first group has 15 invocations/lanes and second group has 17. + let isSecondGroup = index >= 15; + uint4 mask = isSecondGroup ? uint4(0xFFFF8000, 0, 0, 0) : uint4(0x0007FFF, 0, 0, 0); + + isFirstInPartition = (index == 0) || (index == 15); + let isLastInPartition = (index == 14) || (index == 31); + + gSmaller = isSecondGroup ? 2 : 0; + gLarger = isSecondGroup ? 3 : 1; + gMaxValue = isLastInPartition ? gLarger : gSmaller; + + bool result = true + & testMinMax(mask) + ; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-min.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-min.slang new file mode 100644 index 000000000..68e1e9c05 --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-min.slang @@ -0,0 +1,144 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedInclusiveMin subgroupPartitionedInclusiveMinNV +#define __partitionedExclusiveMin subgroupPartitionedExclusiveMinNV +#else +#define __partitionedInclusiveMin WaveMultiPrefixInclusiveMin +#define __partitionedExclusiveMin WaveMultiPrefixExclusiveMin +#endif + +static bool isFirstInPartition = false; +static uint gSmaller = 0; +static uint gLarger = 0; +static uint gMaxValue = 0; + +__generic<T : __BuiltinArithmeticType> +bool test1Min(uint4 mask) +{ + let larger = T(gLarger); + let minValue = T(gMaxValue); + + // The smaller values are set to be the last in the partition, exclusive variants will never get these values. + bool exclusiveRes = true + & (__partitionedExclusiveMin(minValue, mask) == larger) + ; + // Do not check exclusive prefix for the first invocation in partition as their values(identity values) depend on the builtin type `T`. It would be + // nice to have something like T::min or T::max. + if (isFirstInPartition) + { + exclusiveRes = true; + } + + return true + & (__partitionedInclusiveMin(minValue, mask) == minValue) + & exclusiveRes + ; +} + +__generic<T : __BuiltinArithmeticType, let N : int> +bool testVMin(uint4 mask) { + typealias GVec = vector<T, N>; + + let larger = GVec(T(gLarger)); + let minValue = GVec(T(gMaxValue)); + + // The smaller values are set to be the last in the partition, exclusive variants will never get these values. + bool exclusiveRes = true + & all(__partitionedExclusiveMin(minValue, mask) == larger) + ; + // Do not check exclusive prefix for the first invocation in partition as their values(identity values) depend on the builtin type `T`. It would be + // nice to have something like T::min or T::max. + if (isFirstInPartition) + { + exclusiveRes = true; + } + + return true + & all(__partitionedInclusiveMin(minValue, mask) == minValue) + & exclusiveRes + ; +} + +bool testMin(uint4 mask) +{ + return true + & test1Min<int>(mask) + & testVMin<int, 2>(mask) + & testVMin<int, 3>(mask) + & testVMin<int, 4>(mask) + & test1Min<uint>(mask) + & testVMin<uint, 2>(mask) + & testVMin<uint, 3>(mask) + & testVMin<uint, 4>(mask) + & test1Min<float>(mask) + & testVMin<float, 2>(mask) + & testVMin<float, 3>(mask) + & testVMin<float, 4>(mask) + & test1Min<double>(mask) + & testVMin<double, 2>(mask) + & testVMin<double, 3>(mask) + & testVMin<double, 4>(mask) + & test1Min<int8_t>(mask) + & testVMin<int8_t, 2>(mask) + & testVMin<int8_t, 3>(mask) + & testVMin<int8_t, 4>(mask) + & test1Min<int16_t>(mask) + & testVMin<int16_t, 2>(mask) + & testVMin<int16_t, 3>(mask) + & testVMin<int16_t, 4>(mask) + & test1Min<int64_t>(mask) + & testVMin<int64_t, 2>(mask) + & testVMin<int64_t, 3>(mask) + & testVMin<int64_t, 4>(mask) + & test1Min<uint8_t>(mask) + & testVMin<uint8_t, 2>(mask) + & testVMin<uint8_t, 3>(mask) + & testVMin<uint8_t, 4>(mask) + & test1Min<uint16_t>(mask) + & testVMin<uint16_t, 2>(mask) + & testVMin<uint16_t, 3>(mask) + & testVMin<uint16_t, 4>(mask) + & test1Min<uint64_t>(mask) + & testVMin<uint64_t, 2>(mask) + & testVMin<uint64_t, 3>(mask) + & testVMin<uint64_t, 4>(mask) + & test1Min<half>(mask) + & testVMin<half, 2>(mask) + & testVMin<half, 3>(mask) + & testVMin<half, 4>(mask) + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +[MaximallyReconverges] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + let index = dispatchThreadID.x; + + // Split into two groups, first group has 15 invocations/lanes and second group has 17. + let isSecondGroup = index >= 15; + uint4 mask = isSecondGroup ? uint4(0xFFFF8000, 0, 0, 0) : uint4(0x0007FFF, 0, 0, 0); + + isFirstInPartition = (index == 0) || (index == 15); + let isLastInPartition = (index == 14) || (index == 31); + + bool result = true + & testMin(mask) + ; + + gSmaller = isSecondGroup ? 2 : 0; + gLarger = isSecondGroup ? 3 : 1; + gMaxValue = isLastInPartition ? gLarger : gSmaller; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/hlsl-intrinsic/wave-multi-prefix-scalar-functional.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-scalar-functional.slang index 69240198e..5de34b20a 100644 --- a/tests/hlsl-intrinsic/wave-multi-prefix-scalar-functional.slang +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-scalar-functional.slang @@ -10,6 +10,7 @@ RWStructuredBuffer<uint> outputBuffer; [numthreads(8, 1, 1)] +[shader("compute")] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) { uint index = int(dispatchThreadID.x); diff --git a/tests/hlsl-intrinsic/wave-multi-prefix-scalar-functional.slang.expected.txt b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-scalar-functional.slang.expected.txt index c80baa5b1..c80baa5b1 100644 --- a/tests/hlsl-intrinsic/wave-multi-prefix-scalar-functional.slang.expected.txt +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-scalar-functional.slang.expected.txt diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-sum-product.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-sum-product.slang new file mode 100644 index 000000000..bb641cab1 --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix-sum-product.slang @@ -0,0 +1,136 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-slang -compute -dx12 -use-dxil -profile sm_6_5 -shaderobj +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-cuda -compute -shaderobj -xslang -DCUDA + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedInclusiveSum subgroupPartitionedInclusiveAddNV +#define __partitionedInclusiveProduct subgroupPartitionedInclusiveMulNV +#define __partitionedExclusiveSum subgroupPartitionedExclusiveAddNV +#define __partitionedExclusiveProduct subgroupPartitionedExclusiveMulNV +#else +#define __partitionedInclusiveSum WaveMultiPrefixInclusiveSum +#define __partitionedInclusiveProduct WaveMultiPrefixInclusiveProduct +#define __partitionedExclusiveSum WaveMultiPrefixExclusiveSum +#define __partitionedExclusiveProduct WaveMultiPrefixExclusiveProduct +#endif + +static uint partitionedIndex = 0; +static uint gProductValue = 0; + +__generic<T : __BuiltinArithmeticType> +bool test1SumProduct(uint4 mask) +{ + let productValue = T(gProductValue); + + return true + & (__partitionedInclusiveSum(T(1), mask) == T(partitionedIndex + 1)) + & (__partitionedInclusiveProduct(productValue, mask) == productValue) + & (__partitionedExclusiveSum(T(1), mask) == T(partitionedIndex)) + & (__partitionedExclusiveProduct(productValue, mask) == T(1)) + ; +} + +__generic<T : __BuiltinArithmeticType, let N : int> +bool testVSumProduct(uint4 mask) { + typealias GVec = vector<T, N>; + + let productValue = GVec(T(gProductValue)); + + return true + & all(__partitionedInclusiveSum(GVec(T(1)), mask) == GVec(T(partitionedIndex + 1))) + & all(__partitionedInclusiveProduct(productValue, mask) == productValue) + & all(__partitionedExclusiveSum(GVec(T(1)), mask) == GVec(T(partitionedIndex))) + & all(__partitionedExclusiveProduct(productValue, mask) == GVec(T(1))) + ; +} + +bool testSumProduct(uint4 mask) +{ + return true + & test1SumProduct<int>(mask) + & testVSumProduct<int, 2>(mask) + & testVSumProduct<int, 3>(mask) + & testVSumProduct<int, 4>(mask) + & test1SumProduct<uint>(mask) + & testVSumProduct<uint, 2>(mask) + & testVSumProduct<uint, 3>(mask) + & testVSumProduct<uint, 4>(mask) + & test1SumProduct<float>(mask) + & testVSumProduct<float, 2>(mask) + & testVSumProduct<float, 3>(mask) + & testVSumProduct<float, 4>(mask) + & test1SumProduct<double>(mask) + & testVSumProduct<double, 2>(mask) + & testVSumProduct<double, 3>(mask) + & testVSumProduct<double, 4>(mask) + +#if defined(VK) + & test1SumProduct<int8_t>(mask) + & testVSumProduct<int8_t, 2>(mask) + & testVSumProduct<int8_t, 3>(mask) + & testVSumProduct<int8_t, 4>(mask) + & test1SumProduct<uint8_t>(mask) + & testVSumProduct<uint8_t, 2>(mask) + & testVSumProduct<uint8_t, 3>(mask) + & testVSumProduct<uint8_t, 4>(mask) +#endif + +#if !defined(CUDA) + & test1SumProduct<int16_t>(mask) + & testVSumProduct<int16_t, 2>(mask) + & testVSumProduct<int16_t, 3>(mask) + & testVSumProduct<int16_t, 4>(mask) + & test1SumProduct<int64_t>(mask) + & testVSumProduct<int64_t, 2>(mask) + & testVSumProduct<int64_t, 3>(mask) + & testVSumProduct<int64_t, 4>(mask) + & test1SumProduct<uint16_t>(mask) + & testVSumProduct<uint16_t, 2>(mask) + & testVSumProduct<uint16_t, 3>(mask) + & testVSumProduct<uint16_t, 4>(mask) + & test1SumProduct<uint64_t>(mask) + & testVSumProduct<uint64_t, 2>(mask) + & testVSumProduct<uint64_t, 3>(mask) + & testVSumProduct<uint64_t, 4>(mask) + & test1SumProduct<half>(mask) + & testVSumProduct<half, 2>(mask) + & testVSumProduct<half, 3>(mask) + & testVSumProduct<half, 4>(mask) +#endif + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint index = dispatchThreadID.x; + partitionedIndex = index; + bool isSecondGroup = false; + + // Split into two groups, first group has 15 invocations/lanes and second group has 17. + uint4 mask = uint4(0x0007FFF, 0, 0, 0); + if (index >= 15) + { + isSecondGroup = true; + mask = uint4(0xFFFF8000, 0, 0, 0); + partitionedIndex -= 15; + } + + let isLastInPartition = (index == 14) || (index == 31); + gProductValue = isLastInPartition ? uint(0) : uint(1); + + bool result = true + & testSumProduct(mask) + ; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/hlsl-intrinsic/wave-multi-prefix.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix.slang index 99698e497..99698e497 100644 --- a/tests/hlsl-intrinsic/wave-multi-prefix.slang +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-prefix.slang diff --git a/tests/hlsl-intrinsic/wave-multi/wave-multi-sum-product.slang b/tests/hlsl-intrinsic/wave-multi/wave-multi-sum-product.slang new file mode 100644 index 000000000..b40b014f4 --- /dev/null +++ b/tests/hlsl-intrinsic/wave-multi/wave-multi-sum-product.slang @@ -0,0 +1,114 @@ +//TEST_CATEGORY(wave, compute) +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-cuda -compute -shaderobj -xslang -DCUDA + +//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +#if defined(USE_GLSL_SYNTAX) +#define __partitionedSum subgroupPartitionedAddNV +#define __partitionedProduct subgroupPartitionedMulNV +#else +#define __partitionedSum WaveMultiSum +#define __partitionedProduct WaveMultiProduct +#endif + +static uint gSumResult = 0; + +__generic<T : __BuiltinArithmeticType> +bool test1SumProduct(uint4 mask) +{ + let sumResult = T(gSumResult); + + return true + & (__partitionedSum(T(1), mask) == sumResult) + & (__partitionedProduct(T(1), mask) == T(1)) + ; +} + +__generic<T : __BuiltinArithmeticType, let N : int> +bool testVSumProduct(uint4 mask) { + typealias GVec = vector<T, N>; + + let sumResult = GVec(T(gSumResult)); + + return true + & all(__partitionedSum(GVec(T(1)), mask) == sumResult) + & all(__partitionedProduct(GVec(T(1)), mask) == GVec(T(1))) + ; +} + +bool testSumProduct(uint4 mask) +{ + return true + & test1SumProduct<int>(mask) + & testVSumProduct<int, 2>(mask) + & testVSumProduct<int, 3>(mask) + & testVSumProduct<int, 4>(mask) + & test1SumProduct<uint>(mask) + & testVSumProduct<uint, 2>(mask) + & testVSumProduct<uint, 3>(mask) + & testVSumProduct<uint, 4>(mask) + & test1SumProduct<float>(mask) + & testVSumProduct<float, 2>(mask) + & testVSumProduct<float, 3>(mask) + & testVSumProduct<float, 4>(mask) + & test1SumProduct<double>(mask) + & testVSumProduct<double, 2>(mask) + & testVSumProduct<double, 3>(mask) + & testVSumProduct<double, 4>(mask) + +#if !defined(CUDA) + & test1SumProduct<int8_t>(mask) + & testVSumProduct<int8_t, 2>(mask) + & testVSumProduct<int8_t, 3>(mask) + & testVSumProduct<int8_t, 4>(mask) + & test1SumProduct<int16_t>(mask) + & testVSumProduct<int16_t, 2>(mask) + & testVSumProduct<int16_t, 3>(mask) + & testVSumProduct<int16_t, 4>(mask) + & test1SumProduct<int64_t>(mask) + & testVSumProduct<int64_t, 2>(mask) + & testVSumProduct<int64_t, 3>(mask) + & testVSumProduct<int64_t, 4>(mask) + & test1SumProduct<uint8_t>(mask) + & testVSumProduct<uint8_t, 2>(mask) + & testVSumProduct<uint8_t, 3>(mask) + & testVSumProduct<uint8_t, 4>(mask) + & test1SumProduct<uint16_t>(mask) + & testVSumProduct<uint16_t, 2>(mask) + & testVSumProduct<uint16_t, 3>(mask) + & testVSumProduct<uint16_t, 4>(mask) + & test1SumProduct<uint64_t>(mask) + & testVSumProduct<uint64_t, 2>(mask) + & testVSumProduct<uint64_t, 3>(mask) + & testVSumProduct<uint64_t, 4>(mask) + & test1SumProduct<half>(mask) + & testVSumProduct<half, 2>(mask) + & testVSumProduct<half, 3>(mask) + & testVSumProduct<half, 4>(mask) +#endif + ; +} + +[numthreads(32, 1, 1)] +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint index = dispatchThreadID.x; + + // Split into two groups, first group has 15 invocations/lanes and second group has 17. + let isSecondGroup = index >= 15; + uint4 mask = isSecondGroup ? uint4(0xFFFF8000, 0, 0, 0) : uint4(0x0007FFF, 0, 0, 0); + gSumResult = isSecondGroup ? uint(17) : uint(15); + + bool result = true + & testSumProduct(mask) + ; + + // CHECK-COUNT-32: 1 + outputBuffer[index] = uint(result); +} diff --git a/tests/language-feature/capability/testing-framework-with-profiles.slang b/tests/language-feature/capability/testing-framework-with-profiles.slang index 215ba887e..97ff32a9d 100644 --- a/tests/language-feature/capability/testing-framework-with-profiles.slang +++ b/tests/language-feature/capability/testing-framework-with-profiles.slang @@ -17,5 +17,5 @@ RWStructuredBuffer<uint> outputBuffer; void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) { // BUF: 1 - outputBuffer[0] = WaveMaskSum(0xFF, 1); + outputBuffer[0] = WaveActiveSum(1); } |
