From da98acbb43ebf35cf5eb00632c5fd17e317e43a7 Mon Sep 17 00:00:00 2001 From: Alan Baker Date: Thu, 6 Nov 2025 10:40:39 -0500 Subject: [PATCH 1/2] Add subgroup_uniformity language extension * Adds subgroup uniformity language * When supported uniformity analysis is performed at twice (at different scopes) --- wgsl/index.bs | 114 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 32 deletions(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index 8f30f98460..50f8dcdaa7 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -1467,6 +1467,7 @@ The [=language extension=] names are: * `'pointer_composite_access'` * `'uniform_buffer_standard_layout'` * `'subgroup_id'` +* `'subgroup_uniformity'` ### Interpolation Type Names ### {#interpolation-type-names} @@ -1938,6 +1939,15 @@ Extension names are not [=identifiers=]: they do not [=resolves|resolve=] to [=d Allows the use of the [=built-in values/subgroup_id=] and [=built-in values/num_subgroups=] built-in values when the [=extension/subgroups=] extension is enabled. + subgroup_uniformity + + Adds an additional scope, subgroup, for [=uniform control flow=] + [[#subgroup-builtin-functions|subgroup]] and + [[#quad-builtin-functions|quad]] built-in functions to be all invocations + in the same [=subgroup=]. + + Note: The use of subgroup and quad built-in functions requires the + [=extension/subgroups=] extension to be enabled. Note: The intent is that, over time, WGSL will define language extensions embodying all functionality in language extensions commonly supported at that time. @@ -11277,6 +11287,11 @@ For a given group of invocations: in the same [=compute shader stage/workgroup=]. - For other shader stages, the scope of uniform control flow is all invocations for that [=entry point=] in the same [=draw command=]. + - If the [=language_extension/subgroup_uniformity=] feature is supported, + the scope of uniform control flow for + [[#subgroup-builtin-functions|subgroup]] and + [[#quad-builtin-functions|quad]] built-in functions instead is all + invocations in the same [=subgroup=]. - If an expression is executed in uniform control flow, and all invocations compute the same value, it is said to be a uniform value. - If invocations hold the same value for a local variable at every point where it is live, @@ -11287,6 +11302,9 @@ For a given group of invocations: The remaining subsections specify a static analysis that verifies that [[#collective-operations|collective operations]] are only executed in [=uniform control flow=]. +If the [=language_extension/subgroup_uniformity=] feature is supported then +there are multiple scopes of uniformity. +This analysis is performed once for each scope. The analysis assumes [=dynamic errors=] do not occur. A shader stage with a [=dynamic error=] is already non-portable, no matter the outcome @@ -11978,24 +11996,32 @@ Most built-in functions have tags of: Here is the list of exceptions: - A call to a function in [[#sync-builtin-functions]]: - Has a [=function tag=] of [=NoRestriction=]. - - Has a [=call site tag=] of [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] - consisting of an unnamed [=diagnostic/triggering rule=]. - - Note: The triggering rule has no name, and so it cannot be filtered. - - Additionally for the case of a call to [[#workgroupUniformLoad-builtin|workgroupUniformLoad]], + - Has a [=call site tag=] of: + - [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] + consisting of an unnamed [=diagnostic/triggering rule=] + if [=language_extension/subgroup_uniformity=] is not supported or + the scope of [=uniform control flow=] is not subgroup. + - Note: The triggering rule has no name, and so it cannot be filtered. + - [=CallSiteNoRestriction=] otherwise. + - Additionally if [=language_extension/subgroup_uniformity=] is not + supported or the scope of [=uniform control flow=] is not subgroup, for a + call to [[#workgroupUniformLoad-builtin|workgroupUniformLoad]], the parameter `p` has a [=parameter tag=] of [=ParameterRequiredToBeUniform.S|ParameterRequiredToBeUniform.error=], with [=potential-trigger-set=] consisting of an unnamed [=diagnostic/triggering rule=]. - A call to a function in [[#derivative-builtin-functions]], [[#texturesample]], [[#texturesamplebias]], and [[#texturesamplecompare]]: - Has a [=function tag=] of [=ReturnValueMayBeNonUniform=]. - Has a [=call site tag=] as follows: - - Let *DF* be the [=nearest enclosing diagnostic filter=] for the call site location and triggering rule [=trigger/derivative_uniformity=] - - If *DF* exists, then let *S* be the *DF*'s new severity parameter. - - If *S* is the severity [=severity/off=], the call site tag is [=CallSiteNoRestriction=]. - - Otherwise the call site tag is [=CallSiteRequiredToBeUniform.S=], with [=potential-trigger-set=] + - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is not subgroup + - Let *DF* be the [=nearest enclosing diagnostic filter=] for the call site location and triggering rule [=trigger/derivative_uniformity=] + - If *DF* exists, then let *S* be the *DF*'s new severity parameter. + - If *S* is the severity [=severity/off=], the call site tag is [=CallSiteNoRestriction=]. + - Otherwise the call site tag is [=CallSiteRequiredToBeUniform.S=], with [=potential-trigger-set=] + consisting of a [=trigger/derivative_uniformity=] element. + - If there is no such *DF*, + the call site tag is [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] consisting of a [=trigger/derivative_uniformity=] element. - - If there is no such *DF*, - the call site tag is [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] - consisting of a [=trigger/derivative_uniformity=] element. + - [=CallSiteNoRestriction=] otherwise. - A call to [[#textureload]]: - Has a [=call site tag=] of [=CallSiteNoRestriction=] - Has a [=function tag=] as follows: @@ -12004,34 +12030,54 @@ Here is the list of exceptions: - [=NoRestriction=] otherwise - A call to a function in [[#subgroup-builtin-functions]] or [[#quad-builtin-functions]]: - - Has a [=function tag=] of [=ReturnValueMayBeNonUniform=]. + - Has a [=function tag=] of: + - [=NoRestriction=] if the scope of [=uniform control flow=] is subgroup and the function is one of the following: + - [[#subgroupadd-builtin|subgroupAdd]] + - [[#subgroupall-builtin|subgroupAll]] + - [[#subgroupand-builtin|subgroupAnd]] + - [[#subgroupany-builtin|subgroupAny]] + - [[#subgroupballot-builtin|subgroupBallot]] + - [[#subgroupbroadcast-builtin|subgroupBroadcast]] + - [[#subgroupbroadcastfirst-builtin|subgroupBroadcastFirst]] + - [[#subgroupmax-builtin|subgroupMax]] + - [[#subgroupmin-builtin|subgroupMin]] + - [[#subgroupmul-builtin|subgroupMul]] + - [[#subgroupor-builtin|subgroupOr]] + - [[#subgroupxor-builtin|subgroupXor]] + - [=ReturnValueMayBeNonUniform=] otherwise - Let *DF* be the [=nearest enclosing diagnostic filter=] for the call site location and triggering rule [=trigger/subgroup_uniformity=] - Has a [=call site tag=] as follows: - - If *DF* exists, then let *S* be the *DF*'s new severity parameter. - - If *S* is the severity [=severity/off=], the call site tag is [=CallSiteNoRestriction=]. - - Otherwise, the call site tag is [=CallSiteRequiredToBeUniform.S=], with [=potential-trigger-set=] + - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is subgroup then: + - If *DF* exists, then let *S* be the *DF*'s new severity parameter. + - If *S* is the severity [=severity/off=], the call site tag is [=CallSiteNoRestriction=]. + - Otherwise, the call site tag is [=CallSiteRequiredToBeUniform.S=], with [=potential-trigger-set=] + consisting of a [=trigger/subgroup_uniformity=] element. + - If there is no such *DF*, + the call site tag is [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] consisting of a [=trigger/subgroup_uniformity=] element. - - If there is no such *DF*, - the call site tag is [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] - consisting of a [=trigger/subgroup_uniformity=] element. + - [=CallSiteNoRestriction=] otherwise. - Additionally for the case of a call to [[#subgroupshuffleup-builtin|subgroupShuffleUp]] or [[#subgroupshuffledown-builtin|subgroupShuffleDown]], the parameter `delta` has a [=parameter tag=] of: - - If *DF* exists, then let *S* be *DF*'s new severity parameter. - - If *S* is the severity [=severity/off=], the parameter tag is [=NoRestriction=]. - - Otherwise, the parameter tag is [=ParameterRequiredToBeUniform.S=] with [=potential-trigger-set=] - consisting of a [=trigger/subgroup_uniformity=] element. - - If there is no such *DF*, - the parameter tag is [=ParameterRequiredToBeUniform.S|ParameterRequiredToBeUniform.error=], with [=potential-trigger-set=] - consisting of a [=trigger/subgroup_uniformity=] element. + - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is subgroup then: + - If *DF* exists, then let *S* be *DF*'s new severity parameter. + - If *S* is the severity [=severity/off=], the parameter tag is [=NoRestriction=]. + - Otherwise, the parameter tag is [=ParameterRequiredToBeUniform.S=] with [=potential-trigger-set=] + consisting of a [=trigger/subgroup_uniformity=] element. + - If there is no such *DF*, + the parameter tag is [=ParameterRequiredToBeUniform.S|ParameterRequiredToBeUniform.error=], with [=potential-trigger-set=] + consisting of a [=trigger/subgroup_uniformity=] element. + - [=NoRestriction=] otherwise. - Additionally for the case of a call to [[#subgroupshufflexor-builtin|subgroupShuffleXor]], the parameter `mask` has a [=parameter tag=] of: - - If *DF* exists, then let *S* be *DF*'s new severity parameter. - - If *S* is the severity [=severity/off=], the parameter tag is [=NoRestriction=]. - - Otherwise, the parameter tag is [=ParameterRequiredToBeUniform.S=] with [=potential-trigger-set=] - consisting of a [=trigger/subgroup_uniformity=] element. - - If there is no such *DF*, - the parameter tag is [=ParameterRequiredToBeUniform.S|ParameterRequiredToBeUniform.error=], with [=potential-trigger-set=] - consisting of a [=trigger/subgroup_uniformity=] element. + - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is subgroup then: + - If *DF* exists, then let *S* be *DF*'s new severity parameter. + - If *S* is the severity [=severity/off=], the parameter tag is [=NoRestriction=]. + - Otherwise, the parameter tag is [=ParameterRequiredToBeUniform.S=] with [=potential-trigger-set=] + consisting of a [=trigger/subgroup_uniformity=] element. + - If there is no such *DF*, + the parameter tag is [=ParameterRequiredToBeUniform.S|ParameterRequiredToBeUniform.error=], with [=potential-trigger-set=] + consisting of a [=trigger/subgroup_uniformity=] element. + - [=NoRestriction=] otherwise. Note: A WGSL implementation will ensure that if control flow prior to a function call is [=uniform control flow|uniform=], it will also be uniform @@ -12190,6 +12236,10 @@ The following built-in input variables are considered uniform: - [=built-in values/subgroup_size=] when used in a [=compute shader stage=] - [=built-in values/num_subgroups=] +If the scope of [=uniform control flow=] is subgroup, the following built-in +input variables are also considered uniform: +- [=built-in values/subgroup_id=] + All other ones (see [=built-in values=]) are considered non-uniform. Note: An author should avoid grouping the uniform built-in values together with From c5a46a514e6c1f7f367943dade10e40d7339e8f1 Mon Sep 17 00:00:00 2001 From: Alan Baker Date: Sat, 8 Nov 2025 22:00:32 -0500 Subject: [PATCH 2/2] Changes for review * Add uniformity scope and related definitions and update text to use them * Add a note about combining scopes in the analysis --- wgsl/index.bs | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index 50f8dcdaa7..a8a4035537 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -11282,16 +11282,20 @@ The analysis is what actually defines these concepts, and when a program is vali For a given group of invocations: - If all invocations in a given scope execute as if they are executing in lockstep at a given - point in the program, that point is said to have uniform control flow. - - For a [=compute shader stage=], the scope of uniform control flow is all invocations - in the same [=compute shader stage/workgroup=]. - - For other shader stages, the scope of uniform control flow is all invocations for that - [=entry point=] in the same [=draw command=]. - - If the [=language_extension/subgroup_uniformity=] feature is supported, - the scope of uniform control flow for - [[#subgroup-builtin-functions|subgroup]] and + point in the program, that point is said to have uniform control flow + for the given uniformity scope. + - Workgroup uniformity scope: For a [=compute shader + stage=], the uniformity scope is all invocations in the same [=compute + shader stage/workgroup=]. + - Draw uniformity scope: For other shader stages, the + uniformity scope is all invocations for that [=entry point=] in the same + [=draw command=]. + - Subgroup uniformity scope: If the + [=language_extension/subgroup_uniformity=] feature is supported, the + uniformity scope for [[#subgroup-builtin-functions|subgroup]] and [[#quad-builtin-functions|quad]] built-in functions instead is all invocations in the same [=subgroup=]. + - If an expression is executed in uniform control flow, and all invocations compute the same value, it is said to be a uniform value. - If invocations hold the same value for a local variable at every point where it is live, @@ -11303,9 +11307,15 @@ The remaining subsections specify a static analysis that verifies that [[#collective-operations|collective operations]] are only executed in [=uniform control flow=]. If the [=language_extension/subgroup_uniformity=] feature is supported then -there are multiple scopes of uniformity. +there are multiple [=uniformity scopes=]. This analysis is performed once for each scope. +Note: The analysis is described as being executed once per scope, but +implementations may perform a single analysis that includes each scope. +[=Workgroup uniformity scope|Workgroup=] and [=draw uniformity scope|draw=] +uniformity scopes are effectively equivalent since they operate on different +shader stages and represent the largest uniformity scope in a shader stage. + The analysis assumes [=dynamic errors=] do not occur. A shader stage with a [=dynamic error=] is already non-portable, no matter the outcome of uniformity analysis. @@ -12000,11 +12010,11 @@ Here is the list of exceptions: - [=CallSiteRequiredToBeUniform.S|CallSiteRequiredToBeUniform.error=], with [=potential-trigger-set=] consisting of an unnamed [=diagnostic/triggering rule=] if [=language_extension/subgroup_uniformity=] is not supported or - the scope of [=uniform control flow=] is not subgroup. + the [=uniformity scope=] is not subgroup. - Note: The triggering rule has no name, and so it cannot be filtered. - [=CallSiteNoRestriction=] otherwise. - Additionally if [=language_extension/subgroup_uniformity=] is not - supported or the scope of [=uniform control flow=] is not subgroup, for a + supported or the [=uniformity scope=] is not subgroup, for a call to [[#workgroupUniformLoad-builtin|workgroupUniformLoad]], the parameter `p` has a [=parameter tag=] of [=ParameterRequiredToBeUniform.S|ParameterRequiredToBeUniform.error=], with [=potential-trigger-set=] consisting of an unnamed [=diagnostic/triggering rule=]. @@ -12012,7 +12022,7 @@ Here is the list of exceptions: [[#derivative-builtin-functions]], [[#texturesample]], [[#texturesamplebias]], and [[#texturesamplecompare]]: - Has a [=function tag=] of [=ReturnValueMayBeNonUniform=]. - Has a [=call site tag=] as follows: - - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is not subgroup + - If [=language_extension/subgroup_uniformity=] is not supported or the [=uniformity scope=] is not subgroup - Let *DF* be the [=nearest enclosing diagnostic filter=] for the call site location and triggering rule [=trigger/derivative_uniformity=] - If *DF* exists, then let *S* be the *DF*'s new severity parameter. - If *S* is the severity [=severity/off=], the call site tag is [=CallSiteNoRestriction=]. @@ -12031,7 +12041,7 @@ Here is the list of exceptions: - A call to a function in [[#subgroup-builtin-functions]] or [[#quad-builtin-functions]]: - Has a [=function tag=] of: - - [=NoRestriction=] if the scope of [=uniform control flow=] is subgroup and the function is one of the following: + - [=NoRestriction=] if the [=uniformity scope=] is subgroup and the function is one of the following: - [[#subgroupadd-builtin|subgroupAdd]] - [[#subgroupall-builtin|subgroupAll]] - [[#subgroupand-builtin|subgroupAnd]] @@ -12047,7 +12057,7 @@ Here is the list of exceptions: - [=ReturnValueMayBeNonUniform=] otherwise - Let *DF* be the [=nearest enclosing diagnostic filter=] for the call site location and triggering rule [=trigger/subgroup_uniformity=] - Has a [=call site tag=] as follows: - - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is subgroup then: + - If [=language_extension/subgroup_uniformity=] is not supported or the [=uniformity scope=] is subgroup then: - If *DF* exists, then let *S* be the *DF*'s new severity parameter. - If *S* is the severity [=severity/off=], the call site tag is [=CallSiteNoRestriction=]. - Otherwise, the call site tag is [=CallSiteRequiredToBeUniform.S=], with [=potential-trigger-set=] @@ -12058,7 +12068,7 @@ Here is the list of exceptions: - [=CallSiteNoRestriction=] otherwise. - Additionally for the case of a call to [[#subgroupshuffleup-builtin|subgroupShuffleUp]] or [[#subgroupshuffledown-builtin|subgroupShuffleDown]], the parameter `delta` has a [=parameter tag=] of: - - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is subgroup then: + - If [=language_extension/subgroup_uniformity=] is not supported or the [=uniformity scope=] is subgroup then: - If *DF* exists, then let *S* be *DF*'s new severity parameter. - If *S* is the severity [=severity/off=], the parameter tag is [=NoRestriction=]. - Otherwise, the parameter tag is [=ParameterRequiredToBeUniform.S=] with [=potential-trigger-set=] @@ -12069,7 +12079,7 @@ Here is the list of exceptions: - [=NoRestriction=] otherwise. - Additionally for the case of a call to [[#subgroupshufflexor-builtin|subgroupShuffleXor]], the parameter `mask` has a [=parameter tag=] of: - - If [=language_extension/subgroup_uniformity=] is not supported or the scope of [=uniform control flow=] is subgroup then: + - If [=language_extension/subgroup_uniformity=] is not supported or the [=uniformity scope=] is subgroup then: - If *DF* exists, then let *S* be *DF*'s new severity parameter. - If *S* is the severity [=severity/off=], the parameter tag is [=NoRestriction=]. - Otherwise, the parameter tag is [=ParameterRequiredToBeUniform.S=] with [=potential-trigger-set=] @@ -12080,8 +12090,8 @@ Here is the list of exceptions: - [=NoRestriction=] otherwise. Note: A WGSL implementation will ensure that if control flow prior to a -function call is [=uniform control flow|uniform=], it will also be uniform -after the function call. +function call is [=uniform control flow|uniform=] for a particular scope, it +will also be uniform after the function call. ### Uniformity Rules for Expressions ### {#uniformity-expressions} @@ -12236,7 +12246,7 @@ The following built-in input variables are considered uniform: - [=built-in values/subgroup_size=] when used in a [=compute shader stage=] - [=built-in values/num_subgroups=] -If the scope of [=uniform control flow=] is subgroup, the following built-in +At [=subgroup uniformity scope=], the following built-in input variables are also considered uniform: - [=built-in values/subgroup_id=]