From 9aefc3844d363eb9e14f64d498c4e321bacece97 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Wed, 29 Oct 2025 16:45:43 +0800 Subject: [PATCH 01/21] Specify immediate data API in the WebGPU spec Landing the immediate data API specification into the WebGPU spec. Ref to proposal: immediate-data.md. --- spec/index.bs | 141 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 857b139214..f9de7e5849 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -1625,6 +1625,14 @@ A supported limits object has a value for every limit defined by counting any empty slots below the highest index. Validated in {{GPUDevice/createRenderPipeline()}} and [$valid to draw|in draw calls$]. + maxImmediateSize + {{GPUSize32}} [=limit class/maximum=] 64 + + The maximum size, in bytes, of immediate data range used in a pipeline. + Must be a multiple of 4. + + Note: 64 bytes is the size of a 4×4 matrix of f32 values. + maxBindingsPerBindGroup {{GPUSize32}} [=limit class/maximum=] 1000 @@ -1859,6 +1867,7 @@ interface GPUSupportedLimits { readonly attribute unsigned long maxTextureArrayLayers; readonly attribute unsigned long maxBindGroups; readonly attribute unsigned long maxBindGroupsPlusVertexBuffers; + readonly attribute unsigned long maxImmediateSize; readonly attribute unsigned long maxBindingsPerBindGroup; readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout; readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout; @@ -6950,6 +6959,10 @@ GPUPipelineLayout includes GPUObjectBase; : \[[bindGroupLayouts]], of type [=list=]<{{GPUBindGroupLayout}}>, readonly :: The {{GPUBindGroupLayout}} objects provided at creation in {{GPUPipelineLayoutDescriptor/bindGroupLayouts|GPUPipelineLayoutDescriptor.bindGroupLayouts}}. + + : \[[immediateSize]], of type {{GPUSize32}}, readonly + :: + The size in bytes of the immediate data range, provided at creation in {{GPUPipelineLayoutDescriptor/immediateSize|GPUPipelineLayoutDescriptor.immediateSize}}. Note: using the same {{GPUPipelineLayout}} for many {{GPURenderPipeline}} or {{GPUComputePipeline}} pipelines guarantees that the user agent doesn't need to rebind any resources internally when there is a switch between these pipelines. @@ -6979,6 +6992,7 @@ A {{GPUPipelineLayout}} is created via {{GPUDevice/createPipelineLayout()|GPUDev dictionary GPUPipelineLayoutDescriptor : GPUObjectDescriptorBase { required sequence bindGroupLayouts; + GPUSize32 immediateSize = 0; }; @@ -6991,6 +7005,11 @@ pipeline, and have the following members: A list of optional {{GPUBindGroupLayout}}s the pipeline will use. Each element corresponds to a [=@group=] attribute in the {{GPUShaderModule}}, with the `N`th element corresponding with `@group(N)`. + + : immediateSize + :: + The size, in bytes, of the immediate data range used by the pipeline. + Must be a multiple of 4 and must not exceed {{supported limits/maxImmediateSize}}.
@@ -7040,16 +7059,21 @@ pipeline, and have the following members: - The [=list/size=] of |descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}} must be ≤ |limits|.{{supported limits/maxBindGroups}}. - |allEntries| must not [=exceeds the binding slot limits|exceed the binding slot limits=] of |limits|. + - |descriptor|.{{GPUPipelineLayoutDescriptor/immediateSize}} must be ≤ + |limits|.{{supported limits/maxImmediateSize}}. + - |descriptor|.{{GPUPipelineLayoutDescriptor/immediateSize}} must be a multiple of 4. 1. Set the |pl|.{{GPUPipelineLayout/[[bindGroupLayouts]]}} to |bindGroupLayouts|. + 1. Set the |pl|.{{GPUPipelineLayout/[[immediateSize]]}} to |descriptor|.{{GPUPipelineLayoutDescriptor/immediateSize}}.
Note: two {{GPUPipelineLayout}} objects are considered equivalent for any usage if their internal {{GPUPipelineLayout/[[bindGroupLayouts]]}} sequences contain -{{GPUBindGroupLayout}} objects that are [=group-equivalent=]. +{{GPUBindGroupLayout}} objects that are [=group-equivalent=] and their +{{GPUPipelineLayout/[[immediateSize]]}} values are equal. ## Example ## {#bindgroup-examples} @@ -11263,6 +11287,9 @@ interface mixin GPUBindingCommandsMixin { [AllowShared] Uint32Array dynamicOffsetsData, GPUSize64 dynamicOffsetsDataStart, GPUSize32 dynamicOffsetsDataLength); + + undefined setImmediateData(GPUSize32 rangeOffset, AllowSharedBufferSource data, + optional GPUSize64 dataOffset = 0, optional GPUSize32 size); }; @@ -11280,6 +11307,11 @@ It must only be included by interfaces which also include those mixins. : \[[dynamic_offsets]], of type [=ordered map=]<{{GPUIndex32}}, [=list=]<{{GPUBufferDynamicOffset}}>>, initally empty :: The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry. + + : \[[immediate_data]], of type [=ordered map=]<{{GPUSize32}}, [=byte sequence=]>, initially empty + :: + The current immediate data, mapping from byte offset to byte value. + Values are set by {{GPUBindingCommandsMixin/setImmediateData()}}. ## Bind Groups ## {#programmable-passes-bind-groups} @@ -11484,7 +11516,6 @@ It must only be included by interfaces which also include those mixins. -
To Iterate over each dynamic binding offset in a given {{GPUBindGroup}} |bindGroup| with a given list of |steps| to be executed for each dynamic offset, run the following [=device timeline=] steps: @@ -11573,6 +11604,38 @@ It must only be included by interfaces which also include those mixins. Otherwise return `true`.
+
+ Validate immediate data(encoder, pipeline) + + **Arguments:** + + : {{GPUBindingCommandsMixin}} |encoder| + :: Encoder whose immediate data is being validated. + : {{GPUPipelineBase}} |pipeline| + :: Pipeline to validate |encoder|'s immediate data is compatible with. + + [=Device timeline=] steps: + + 1. If any of the following conditions are unsatisfied, return `false`: + +
+ - |pipeline| must not be `null`. + - Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. + - Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. + - If |immediateSize| is 0, return `true`. + - For each immediate data variable that is [=statically used=] by any entry point in |pipeline|: + - Let |T| be the [=store type=] of the immediate data variable. + - Let |requiredSize| be [$SizeOf$](|T|). + - |immediateSize| must be ≥ |requiredSize|. + - For each 32-bit word (4-byte aligned slot) in the range [0, |requiredSize|) that + contains actual data (excluding padding bytes): + - Let |slotIndex| be the byte offset of the slot divided by 4. + - |encoder|.{{GPUBindingCommandsMixin/[[immediate_data]]}} must [=map/contain=] |slotIndex|. +
+ + Otherwise return `true`. +
+
Encoder bind groups alias a writable resource(|encoder|, |pipeline|) if any writable buffer binding range overlaps with any other binding range of the same buffer, @@ -11630,6 +11693,73 @@ It must only be included by interfaces which also include those mixins. Implementations are strongly encouraged to optimize this algorithm.
+## Immediate Data ## {#programmable-passes-immediate-data} + +
+ : setImmediateData(rangeOffset, data, dataOffset, size) + :: + Sets immediate data for subsequent render or compute commands. + +
+
+ **Called on:** {{GPUBindingCommandsMixin}} |this|. + + **Arguments:** + +
+                    |rangeOffset|: Offset in bytes into the immediate data range to begin writing at.
+                        Must be a multiple of 4.
+                    |data|: Data to write into the immediate data range.
+                    |dataOffset|: Offset into |data| to begin writing from. Given in elements if
+                        |data| is a {{TypedArray}} and bytes otherwise. Defaults to 0.
+                    |size|: Size of content to write from |data|. Given in elements if
+                        |data| is a {{TypedArray}} and bytes otherwise.
+                
+ + **Returns:** {{undefined}} + + [=Content timeline=] steps: + + 1. If |data| is an {{ArrayBuffer}} or {{DataView}}, let the element type be "byte". + Otherwise, |data| is a TypedArray; let the element type be the type of the TypedArray. + 1. Let |dataSize| be the size of |data|, in elements. + 1. If |size| is missing, + let |contentsSize| be |dataSize| − |dataOffset|. + Otherwise, let |contentsSize| be |size|. + 1. If any of the following conditions are unsatisfied, + throw an {{OperationError}} and return. + +
+ - |contentsSize| ≥ 0. + - |dataOffset| + |contentsSize| ≤ |dataSize|. + - |rangeOffset| is a multiple of 4. + - |contentsSize|, converted to bytes, is a multiple of 4. +
+ 1. Let |dataContents| be [=get a copy of the buffer source|a copy of the bytes held by the buffer source=] |data|. + 1. Let |contents| be the |contentsSize| elements of |dataContents| starting at + an offset of |dataOffset| elements. + 1. Let |contentsBytes| be |contentsSize| converted to bytes. + 1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}. +
+
+ [=Device timeline=] steps: + + 1. [$Validate the encoder state$] of |this|. If it returns false, return. + 1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return. + +
+ - |rangeOffset| + |contentsBytes| ≤ |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxImmediateSize}}. +
+ 1. For each byte |b| at index |i| in |contents|: + 1. Let |slotIndex| be floor((|rangeOffset| + |i|) ÷ 4). + 1. Let |byteInSlot| be (|rangeOffset| + |i|) mod 4. + 1. If |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}} does not [=map/contain=] |slotIndex|: + 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|slotIndex|] to a new [=list=] of 4 bytes initialized to 0. + 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|slotIndex|][|byteInSlot|] to |b|. +
+
+
+ # Debug Markers # {#debug-markers} GPUDebugCommandsMixin provides methods to apply debug labels to groups @@ -11918,6 +12048,8 @@ dictionary GPUComputePassDescriptor - |usageScope| must satisfy [=usage scope validation=]. - [$Validate encoder bind groups$](|this|, |this|.{{GPUComputePassEncoder/[[pipeline]]}}) is `true`. + - [$Validate immediate data$](|this|, |this|.{{GPUComputePassEncoder/[[pipeline]]}}) + is `true`. - all of |workgroupCountX|, |workgroupCountY| and |workgroupCountZ| are ≤ |this|.device.limits.{{supported limits/maxComputeWorkgroupsPerDimension}}. - let |workgroupSize| be the computed workgroup size for @@ -11997,6 +12129,8 @@ dictionary GPUComputePassDescriptor - |usageScope| must satisfy [=usage scope validation=]. - [$Validate encoder bind groups$](|this|, |this|.{{GPUComputePassEncoder/[[pipeline]]}}) is `true`. + - [$Validate immediate data$](|this|, |this|.{{GPUComputePassEncoder/[[pipeline]]}}) + is `true`. - |indirectBuffer| is [$valid to use with$] |this|. - |indirectBuffer|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/INDIRECT}}. - |indirectOffset| + sizeof([=indirect dispatch parameters=]) ≤ @@ -13413,6 +13547,8 @@ It must only be included by interfaces which also include those mixins.
- [$Validate encoder bind groups$](|encoder|, |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}}) must be `true`. + - [$Validate immediate data$](|encoder|, |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}}) + must be `true`. - Let |pipelineDescriptor| be |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}}.{{GPURenderPipeline/[[descriptor]]}}. - For each {{GPUIndex32}} |slot| `0` to |pipelineDescriptor|.{{GPURenderPipelineDescriptor/vertex}}.{{GPUVertexState/buffers}}.[=list/size=]: @@ -13797,6 +13933,7 @@ attachments used by this encoder. the following [=device timeline=] steps: 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}. + 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[immediate_data]]}}. 1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`. 1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}. From 5f99456f3222946f85563e8976bad73227ad0f74 Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Wed, 29 Oct 2025 16:53:35 +0800 Subject: [PATCH 02/21] Update spec/index.bs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- spec/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/index.bs b/spec/index.bs index f9de7e5849..8cf94fcb73 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -11308,7 +11308,7 @@ It must only be included by interfaces which also include those mixins. :: The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry. - : \[[immediate_data]], of type [=ordered map=]<{{GPUSize32}}, [=byte sequence=]>, initially empty + : \[[immediate_data]], of type [=ordered map=]<{{GPUSize32}}, [=list=]<[=byte=]>>, initially empty :: The current immediate data, mapping from byte offset to byte value. Values are set by {{GPUBindingCommandsMixin/setImmediateData()}}. From bffe39dbc48e0d49ece86609842052caa4575d4c Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Wed, 29 Oct 2025 16:53:53 +0800 Subject: [PATCH 03/21] Update spec/index.bs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- spec/index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 8cf94fcb73..e17c385d4c 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -11616,13 +11616,13 @@ It must only be included by interfaces which also include those mixins. [=Device timeline=] steps: - 1. If any of the following conditions are unsatisfied, return `false`: + 1. If |immediateSize| is 0, return `true`. + 2. If any of the following conditions are unsatisfied, return `false`:
- |pipeline| must not be `null`. - Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. - Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. - - If |immediateSize| is 0, return `true`. - For each immediate data variable that is [=statically used=] by any entry point in |pipeline|: - Let |T| be the [=store type=] of the immediate data variable. - Let |requiredSize| be [$SizeOf$](|T|). From ad13b13cd7630fee02c06349f514002edb60d23c Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Wed, 29 Oct 2025 16:41:07 +0800 Subject: [PATCH 04/21] Specify address space Update index.bs to include the new address space. Ref to proposal: immediate-data.md. --- wgsl/index.bs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index 61201dd777..b1ae4299ad 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -4910,6 +4910,14 @@ effective-value-type. Yes.
[=uniform buffer=] +[=variable|var=]<[=address spaces/immediate=]> + Immutable + [=module scope|Module=] + [=type/concrete|Concrete=] [=constructible=] [=host-shareable=], excluding arrays and structures containing array members + Disallowed + + Yes.
[=immediate data=] + [=variable|var=]6 Immutable7 [=module scope|Module=] @@ -5194,6 +5202,12 @@ Its [=store type=] must be a [=host-shareable=] type and must satisfy the The variable may be declared with a [=access/read=] or [=access/read_write=] access mode; the default is read. +A variable in the [=address spaces/immediate=] address space is an immediate data variable. +Its [=store type=] must be a [=host-shareable=] [=constructible=] [=type/concrete=] type, +excluding arrays and structures that contain array members. +Each [=entry point=] may [=statically access=] at most one immediate data variable. + A texture resource is a variable whose [=effective-value-type=] is a [=texture type=]. It is declared at [=module scope=]. It holds an opaque handle which is used to access the underlying grid of [=texels=] in a [=texture=]. @@ -7620,17 +7634,17 @@ path: syntax/for_init.syntax.bs.include path: syntax/for_update.syntax.bs.include -The for statement is syntactic sugar over a [=compound statement=] +The for statement is syntactic sugar over a [=compound statement=] containing a [=statement/loop=] statement. -In general, the `for` statement takes the form +In general, the `for` statement takes the form > `for (` *initializer* `;` *condition* `;` *update_part* `) {` *body* `}` When the condition expression is present, the `for` statement desugars to a loop of the form:
- {
    - *initializer* ;
    + {
    + *initializer* ;
    loop {
        if !(*condition*) { break; }
        *body*
        @@ -7641,8 +7655,8 @@ When the condition expression is present, the `for` statement desugars to a loop When the condition expression is absent, the `for` statement desugars to a loop of the form:
- {
    - *initializer* ;
    + {
    + *initializer* ;
    loop {
        *body*
        continuing { *update_part* }
    @@ -10595,6 +10609,12 @@ table. Invocations in the same [=shader stage=] [=access/read=] For [=storage buffer=] variables + immediate + Invocations in the same [=shader stage=] + [=access/read=] + For [=immediate data=] variables.
+ [=type/concrete|Concrete=] [=constructible=] [=host-shareable=] types, excluding arrays and structures containing array members.
+ Each [=entry point=] can [=statically access=] at most one immediate variable. handle Invocations in the same shader stage [=access/read=] @@ -11129,7 +11149,7 @@ sections and then the resulting layout is validated against the [=RequiredAlignOf=](|T|, |C|) rules. --> -When [=language_extension/uniform_buffer_standard_layout=] is **not** supported, +When [=language_extension/uniform_buffer_standard_layout=] is **not** supported, the [=address spaces/uniform=] address space requires that: * Array elements are aligned to 16 byte boundaries. That is, [=StrideOf=](array<|T|,|N|>) = 16 × |k|' for some positive integer |k|'. From 895beb80c5aa4c5dc00b2882010f4a72a07107ea Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Fri, 31 Oct 2025 09:37:15 +0800 Subject: [PATCH 05/21] Update wgsl/index.bs Co-authored-by: alan-baker --- wgsl/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index b1ae4299ad..c545a138fe 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -5204,7 +5204,7 @@ access mode; the default is read. A variable in the [=address spaces/immediate=] address space is an immediate data variable. -Its [=store type=] must be a [=host-shareable=] [=constructible=] [=type/concrete=] type, +Its [=store type=] [=shader-creation error|must=] be a [=host-shareable=] [=constructible=] [=type/concrete=] type, excluding arrays and structures that contain array members. Each [=entry point=] may [=statically access=] at most one immediate data variable. From 21ba7fa3b76c34269b5ac2c5331ed2a34080b2c6 Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Fri, 31 Oct 2025 09:37:23 +0800 Subject: [PATCH 06/21] Update wgsl/index.bs Co-authored-by: alan-baker --- wgsl/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index c545a138fe..0f256f4053 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -5206,7 +5206,7 @@ A variable in the [=address spaces/immediate=] address space is an immediate data variable. Its [=store type=] [=shader-creation error|must=] be a [=host-shareable=] [=constructible=] [=type/concrete=] type, excluding arrays and structures that contain array members. -Each [=entry point=] may [=statically access=] at most one immediate data variable. +Each [=entry point=] [=shader-creation error|must=] [=statically access=] at most one immediate data variable. A texture resource is a variable whose [=effective-value-type=] is a [=texture type=]. It is declared at [=module scope=]. From b045b818b25774e5a6ee0d8566001f82ae777d7b Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Mon, 10 Nov 2025 13:43:12 +0800 Subject: [PATCH 07/21] Address comments --- spec/index.bs | 30 ++++++++++++++---------- wgsl/index.bs | 63 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index e17c385d4c..5d453f05da 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -159,6 +159,7 @@ spec: WGSL; urlPrefix: https://gpuweb.github.io/gpuweb/wgsl/# text: subgroup size; url: subgroup-size type: abstract-op text: SizeOf; url: sizeof + text: AccessibleSlots; url: accessible-slots spec: Internationalization Glossary; urlPrefix: https://www.w3.org/TR/i18n-glossary/# type: dfn text: localizable text; url: dfn-localizable-text @@ -11289,7 +11290,7 @@ interface mixin GPUBindingCommandsMixin { GPUSize32 dynamicOffsetsDataLength); undefined setImmediateData(GPUSize32 rangeOffset, AllowSharedBufferSource data, - optional GPUSize64 dataOffset = 0, optional GPUSize32 size); + optional GPUSize64 dataOffset = 0, optional GPUSize64 size); }; @@ -11308,10 +11309,17 @@ It must only be included by interfaces which also include those mixins. :: The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry. - : \[[immediate_data]], of type [=ordered map=]<{{GPUSize32}}, [=list=]<[=byte=]>>, initially empty + : \[[immediate_data]], of type [=byte sequence=], initially empty :: - The current immediate data, mapping from byte offset to byte value. + The current immediate data bytes. + The length equals the device's {{supported limits/maxImmediateSize}}. Values are set by {{GPUBindingCommandsMixin/setImmediateData()}}. + + : \[[immediate_data_set]], of type [=list=]<{{boolean}}>, initially empty + :: + Tracks which 32-bit word slots of immediate data have been set. + The length equals the device's {{supported limits/maxImmediateSize}} divided by 4. + Each entry corresponds to a 4-byte slot and is initially `false`. ## Bind Groups ## {#programmable-passes-bind-groups} @@ -11627,10 +11635,9 @@ It must only be included by interfaces which also include those mixins. - Let |T| be the [=store type=] of the immediate data variable. - Let |requiredSize| be [$SizeOf$](|T|). - |immediateSize| must be ≥ |requiredSize|. - - For each 32-bit word (4-byte aligned slot) in the range [0, |requiredSize|) that - contains actual data (excluding padding bytes): - - Let |slotIndex| be the byte offset of the slot divided by 4. - - |encoder|.{{GPUBindingCommandsMixin/[[immediate_data]]}} must [=map/contain=] |slotIndex|. + - Let |accessibleSlots| be [$AccessibleSlots$](|T|) as defined in [[WGSL]]. + - For each |slotIndex| in |accessibleSlots|: + - |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}[|slotIndex|] must be `true`.
Otherwise return `true`. @@ -11751,11 +11758,10 @@ It must only be included by interfaces which also include those mixins. - |rangeOffset| + |contentsBytes| ≤ |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxImmediateSize}}.
1. For each byte |b| at index |i| in |contents|: - 1. Let |slotIndex| be floor((|rangeOffset| + |i|) ÷ 4). - 1. Let |byteInSlot| be (|rangeOffset| + |i|) mod 4. - 1. If |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}} does not [=map/contain=] |slotIndex|: - 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|slotIndex|] to a new [=list=] of 4 bytes initialized to 0. - 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|slotIndex|][|byteInSlot|] to |b|. + 1. Let |byteIndex| be |rangeOffset| + |i|. + 1. Let |slotIndex| be floor(|byteIndex| ÷ 4). + 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|byteIndex|] to |b|. + 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}[|slotIndex|] to true. diff --git a/wgsl/index.bs b/wgsl/index.bs index 0f256f4053..f1a0b77ba8 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -275,6 +275,7 @@ spec: WebGPU; urlPrefix: https://gpuweb.github.io/gpuweb/# text: texture view swizzle; url: dom-gputextureviewdescriptor-swizzle for: supported limits text: maxComputeWorkgroupStorageSize; url: dom-supported-limits-maxcomputeworkgroupstoragesize + text: maxImmediateSize; url: dom-supported-limits-maximmediatesize type: attribute for: GPU text: wgslLanguageFeatures; url: gpuwgsllanguagefeatures @@ -942,6 +943,12 @@ shader that goes beyond the specified limits. This maps the WebGPU [=supported limits/maxComputeWorkgroupStorageSize=] limit into a standalone WGSL limit. [=supported limits/maxComputeWorkgroupStorageSize|16384=] + Maximum combined [=byte-size=] of all [=immediate data=] variables that are + [=statically accessed=] by a single [=shader=] + + This maps the WebGPU [=supported limits/maxImmediateSize=] limit + into a standalone WGSL limit. + [=supported limits/maxImmediateSize|64=] Maximum number of elements in [[#value-constructor-builtin-function|value constructor]] expression of [=array=] type2047 @@ -1499,6 +1506,7 @@ The [=language extension=] names are: * `'texture_and_sampler_let'` * `'texture_formats_tier1'` * `'linear_indexing'` +* `'immediate_data'` ### Interpolation Type Names ### {#interpolation-type-names} @@ -1998,6 +2006,11 @@ Extension names are not [=identifiers=]: they do not [=resolves|resolve=] to [=d Supports the [=built-in values/global_invocation_index=] and [=built-in values/workgroup_index=] [=built-in values=]. + immediate_data + + Enables the [=address spaces/immediate=] address space, allowing variables + to be declared with `var` and bound to small amounts of frequently + updated data passed directly from the command encoder via the WebGPU API. @@ -4913,7 +4926,7 @@ effective-value-type. [=variable|var=]<[=address spaces/immediate=]> Immutable [=module scope|Module=] - [=type/concrete|Concrete=] [=constructible=] [=host-shareable=], excluding arrays and structures containing array members + [=constructible=] [=host-shareable=], excluding arrays and structures containing array members Disallowed Yes.
[=immediate data=] @@ -5204,10 +5217,41 @@ access mode; the default is read. A variable in the [=address spaces/immediate=] address space is an immediate data variable. -Its [=store type=] [=shader-creation error|must=] be a [=host-shareable=] [=constructible=] [=type/concrete=] type, +Its [=store type=] [=shader-creation error|must=] be a [=host-shareable=] [=constructible=] type, excluding arrays and structures that contain array members. Each [=entry point=] [=shader-creation error|must=] [=statically access=] at most one immediate data variable. +The accessible slots of an immediate data variable are the 4-byte-aligned slots that +contain the actual data (not padding bytes) of the variable's [=store type=]. +Let |T| be the store type of an immediate data variable. +The set of accessible slots is computed as follows: + +
+ AccessibleSlots(|T|) computes a [=set=] of slot indices (where each slot is 4 bytes): + + 1. Let |slots| be an empty [=set=]. + 1. Let |size| = [=SizeOf=](|T|). + 1. If |T| is a [=structure=] with |N| members: + 1. For each member index |i| from 1 to |N|: + 1. Let |offset| = [=OffsetOfMember=](|T|, |i|). + 1. Let |memberSize| = [=SizeOfMember=](|T|, |i|). + 1. For each byte offset |b| from |offset| to |offset| + |memberSize| - 1: + 1. Let |slotIndex| = floor(|b| ÷ 4). + 1. Add |slotIndex| to |slots|. + 1. Otherwise (|T| is not a structure): + 1. For each byte offset |b| from 0 to |size| - 1: + 1. Let |slotIndex| = floor(|b| ÷ 4). + 1. Add |slotIndex| to |slots|. + 1. Return |slots|. +
+ +
+The [=accessible slots=] exclude padding bytes between structure members and at the end of structures. +Only the accessible slots need to be initialized via the WebGPU API +before the immediate data variable is accessed in a shader. +
+ + A texture resource is a variable whose [=effective-value-type=] is a [=texture type=]. It is declared at [=module scope=]. It holds an opaque handle which is used to access the underlying grid of [=texels=] in a [=texture=]. @@ -5221,7 +5265,7 @@ It is declared at [=module scope=], exists in the [=address spaces/handle=] addr and is immutable. As described in [[#resource-interface]], uniform buffers, storage buffers, -textures, and samplers form the [=resource interface of a shader=]. +textures, samplers, and immediate data form the [=resource interface of a shader=]. The lifetime of a variable is the period during shader execution for which the [=memory locations=] are associated with the variable. @@ -10321,23 +10365,28 @@ A resource is an object which provides access to data extern and which is not an [=override-declaration=] and not a [[#stage-inputs-outputs|shader stage input or output]]. Resources are shared by all invocations of the shader. -There are four kinds of resources: +There are five kinds of resources: * [=Uniform buffers=] * [=Storage buffers=] * [=Texture resources=] * [=Sampler resources=] +* [=Immediate data=] The resource interface of a shader is the set of module-scope resource variables [=statically accessed=] by [=functions in a shader stage|functions in the shader stage=]. -Each resource variable [=shader-creation error|must=] be declared with both [=attribute/group=] and [=attribute/binding=] +Each resource variable, except for [=immediate data=] variables, [=shader-creation error|must=] be declared with both [=attribute/group=] and [=attribute/binding=] attributes. Together with the shader's stage, these identify the binding address of the resource on the shader's pipeline. See [[WebGPU#gpupipelinelayout]]. +[=Immediate data=] variables do not use [=attribute/group=] or [=attribute/binding=] attributes. +They are accessed via the WebGPU API on the command encoder, +and are limited in size by the pipeline layout's immediate data size configuration. + Two different resource variables in a [=shader=] [=shader-creation error|must not=] have the same [=attribute/group=] and [=attribute/binding=] values, when considered as a pair. @@ -10398,6 +10447,8 @@ where compatibility is defined by the following table. [=type/external texture=] {{GPUBindGroupLayoutEntry/externalTexture}} (not applicable) + [=immediate data=] + Not a bind group entry. Validated against pipeline layout immediate data size. See the [$validating GPUProgrammableStage|WebGPU API$] @@ -10613,7 +10664,7 @@ table. Invocations in the same [=shader stage=] [=access/read=] For [=immediate data=] variables.
- [=type/concrete|Concrete=] [=constructible=] [=host-shareable=] types, excluding arrays and structures containing array members.
+ [=constructible=] [=host-shareable=] types, excluding arrays and structures containing array members.
Each [=entry point=] can [=statically access=] at most one immediate variable. handle Invocations in the same shader stage From f2653d8fb6cccab65d9f631318822c5d642cb7db Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Mon, 1 Dec 2025 10:30:50 +0800 Subject: [PATCH 08/21] Address comments --- spec/index.bs | 12 ++++++------ wgsl/index.bs | 33 ++++++++------------------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 5d453f05da..74288d3032 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -11289,7 +11289,7 @@ interface mixin GPUBindingCommandsMixin { GPUSize64 dynamicOffsetsDataStart, GPUSize32 dynamicOffsetsDataLength); - undefined setImmediateData(GPUSize32 rangeOffset, AllowSharedBufferSource data, + undefined setImmediates(GPUSize32 rangeOffset, AllowSharedBufferSource data, optional GPUSize64 dataOffset = 0, optional GPUSize64 size); }; @@ -11313,7 +11313,7 @@ It must only be included by interfaces which also include those mixins. :: The current immediate data bytes. The length equals the device's {{supported limits/maxImmediateSize}}. - Values are set by {{GPUBindingCommandsMixin/setImmediateData()}}. + Values are set by {{GPUBindingCommandsMixin/setImmediates()}}. : \[[immediate_data_set]], of type [=list=]<{{boolean}}>, initially empty :: @@ -11703,17 +11703,17 @@ It must only be included by interfaces which also include those mixins. ## Immediate Data ## {#programmable-passes-immediate-data}
- : setImmediateData(rangeOffset, data, dataOffset, size) + : setImmediates(rangeOffset, data, dataOffset, size) :: Sets immediate data for subsequent render or compute commands. -
+
**Called on:** {{GPUBindingCommandsMixin}} |this|. **Arguments:** -
+                
                     |rangeOffset|: Offset in bytes into the immediate data range to begin writing at.
                         Must be a multiple of 4.
                     |data|: Data to write into the immediate data range.
@@ -11739,7 +11739,6 @@ It must only be included by interfaces which also include those mixins.
                     
- |contentsSize| ≥ 0. - |dataOffset| + |contentsSize| ≤ |dataSize|. - - |rangeOffset| is a multiple of 4. - |contentsSize|, converted to bytes, is a multiple of 4.
1. Let |dataContents| be [=get a copy of the buffer source|a copy of the bytes held by the buffer source=] |data|. @@ -11755,6 +11754,7 @@ It must only be included by interfaces which also include those mixins. 1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.
+ - |rangeOffset| is a multiple of 4. - |rangeOffset| + |contentsBytes| ≤ |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxImmediateSize}}.
1. For each byte |b| at index |i| in |contents|: diff --git a/wgsl/index.bs b/wgsl/index.bs index f1a0b77ba8..e3f2ddbf38 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -5221,32 +5221,8 @@ Its [=store type=] [=shader-creation error|must=] be a [=host-shareable=] [=cons excluding arrays and structures that contain array members. Each [=entry point=] [=shader-creation error|must=] [=statically access=] at most one immediate data variable. -The accessible slots of an immediate data variable are the 4-byte-aligned slots that -contain the actual data (not padding bytes) of the variable's [=store type=]. -Let |T| be the store type of an immediate data variable. -The set of accessible slots is computed as follows: - -
- AccessibleSlots(|T|) computes a [=set=] of slot indices (where each slot is 4 bytes): - - 1. Let |slots| be an empty [=set=]. - 1. Let |size| = [=SizeOf=](|T|). - 1. If |T| is a [=structure=] with |N| members: - 1. For each member index |i| from 1 to |N|: - 1. Let |offset| = [=OffsetOfMember=](|T|, |i|). - 1. Let |memberSize| = [=SizeOfMember=](|T|, |i|). - 1. For each byte offset |b| from |offset| to |offset| + |memberSize| - 1: - 1. Let |slotIndex| = floor(|b| ÷ 4). - 1. Add |slotIndex| to |slots|. - 1. Otherwise (|T| is not a structure): - 1. For each byte offset |b| from 0 to |size| - 1: - 1. Let |slotIndex| = floor(|b| ÷ 4). - 1. Add |slotIndex| to |slots|. - 1. Return |slots|. -
-
-The [=accessible slots=] exclude padding bytes between structure members and at the end of structures. +The [=AccessibleSlots=] exclude padding bytes between structure members and at the end of structures. Only the accessible slots need to be initialized via the WebGPU API before the immediate data variable is accessed in a shader.
@@ -10737,6 +10713,13 @@ and |A| is a host-shareable or fixed footprint array or runtime-sized array: [=StrideOf=](array<|E|, |N|>) = [=roundUp=]([=AlignOf=](E), [=SizeOf=](E))
[=StrideOf=](array<|E|>) = [=roundUp=]([=AlignOf=](E), [=SizeOf=](E))

+* AccessibleBytes(|T|) is the set of byte offsets within an instance of type |T| that contain data. + * If |T| is a scalar or vector, then [=AccessibleBytes=](|T|) is the set of integers `k` such that `0 <= k <` [=SizeOf=](|T|). + * If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(vecR) }` for `i` in `0..C-1`, where `Stride` is [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|)). + * If |T| is an atomic type `atomic`, then [=AccessibleBytes=](|T|) is [=AccessibleBytes=](|T'|). + * If |T| is an array of |N| elements of type |E|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(E) }` for `i` in `0..N-1`, where `Stride` is [=StrideOf=](|T|). + * If |T| is a structure |S|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + Offset | k in AccessibleBytes(M_i) }` for each member `M_i` at offset `Offset` = [=OffsetOfMember=](|S|, `i`). +* AccessibleSlots(|T|) is the set of integers `i` such that the intersection of the interval `[4*i, 4*i+4)` and [=AccessibleBytes=](|T|) is not empty. ### Alignment and Size ### {#alignment-and-size} From d33528edf71b932cddb81e9978f601103581f903 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Mon, 1 Dec 2025 11:08:31 +0800 Subject: [PATCH 09/21] Address comments --- wgsl/index.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index e3f2ddbf38..1d6a68905c 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -943,8 +943,9 @@ shader that goes beyond the specified limits. This maps the WebGPU [=supported limits/maxComputeWorkgroupStorageSize=] limit into a standalone WGSL limit. [=supported limits/maxComputeWorkgroupStorageSize|16384=] - Maximum combined [=byte-size=] of all [=immediate data=] variables that are - [=statically accessed=] by a single [=shader=] + Maximum combined [=byte-size=] of all [=variables=] instantiated in the + [=address spaces/immediate=] address space that are [=statically accessed=] by a single + [=shader=] This maps the WebGPU [=supported limits/maxImmediateSize=] limit into a standalone WGSL limit. From b8243f4a1a8301ee5e8b84763d2d16978fb5c0ec Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Mon, 1 Dec 2025 11:22:13 +0800 Subject: [PATCH 10/21] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- spec/index.bs | 12 ++++++------ wgsl/index.bs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 74288d3032..70c5a0cc74 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -11305,7 +11305,7 @@ It must only be included by interfaces which also include those mixins. :: The current {{GPUBindGroup}} for each index. - : \[[dynamic_offsets]], of type [=ordered map=]<{{GPUIndex32}}, [=list=]<{{GPUBufferDynamicOffset}}>>, initally empty + : \[[dynamic_offsets]], of type [=ordered map=]<{{GPUIndex32}}, [=list=]<{{GPUBufferDynamicOffset}}>>, initially empty :: The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry. @@ -11624,13 +11624,13 @@ It must only be included by interfaces which also include those mixins. [=Device timeline=] steps: - 1. If |immediateSize| is 0, return `true`. - 2. If any of the following conditions are unsatisfied, return `false`: + 1. Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. + 2. Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. + 3. If |immediateSize| is 0, return `true`. + 4. If any of the following conditions are unsatisfied, return `false`:
- |pipeline| must not be `null`. - - Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. - - Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. - For each immediate data variable that is [=statically used=] by any entry point in |pipeline|: - Let |T| be the [=store type=] of the immediate data variable. - Let |requiredSize| be [$SizeOf$](|T|). @@ -13939,7 +13939,7 @@ attachments used by this encoder. the following [=device timeline=] steps: 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}. - 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[immediate_data]]}}. + 1. Clear |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}. 1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`. 1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}. diff --git a/wgsl/index.bs b/wgsl/index.bs index 1d6a68905c..46941c7a99 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -4927,7 +4927,7 @@ effective-value-type. [=variable|var=]<[=address spaces/immediate=]> Immutable [=module scope|Module=] - [=constructible=] [=host-shareable=], excluding arrays and structures containing array members + [=type/concrete|Concrete=] [=constructible=] [=host-shareable=], excluding arrays and structures containing array members Disallowed Yes.
[=immediate data=] From 5ab1e43c56e98af9db89407f6f198ea1a12b143d Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Mon, 1 Dec 2025 11:23:18 +0800 Subject: [PATCH 11/21] Update wgsl/index.bs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- wgsl/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index 46941c7a99..eb1c47fb05 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -10716,7 +10716,7 @@ and |A| is a host-shareable or fixed footprint array or runtime-sized array:

* AccessibleBytes(|T|) is the set of byte offsets within an instance of type |T| that contain data. * If |T| is a scalar or vector, then [=AccessibleBytes=](|T|) is the set of integers `k` such that `0 <= k <` [=SizeOf=](|T|). - * If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(vecR) }` for `i` in `0..C-1`, where `Stride` is [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|)). + * If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(vec|R|) }` for `i` in `0..C-1`, where `Stride` is [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|)). * If |T| is an atomic type `atomic`, then [=AccessibleBytes=](|T|) is [=AccessibleBytes=](|T'|). * If |T| is an array of |N| elements of type |E|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(E) }` for `i` in `0..N-1`, where `Stride` is [=StrideOf=](|T|). * If |T| is a structure |S|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + Offset | k in AccessibleBytes(M_i) }` for each member `M_i` at offset `Offset` = [=OffsetOfMember=](|S|, `i`). From 08c755ad250a01f00579d5f0424feda2df7ef2da Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 10 Dec 2025 17:56:40 -0500 Subject: [PATCH 12/21] WGSL: don't describe immediate data as part of the resource interface They are their own thing in the shader interface, similar to override declarations --- wgsl/index.bs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index eb1c47fb05..392a84a7ee 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -260,6 +260,7 @@ spec: WebGPU; urlPrefix: https://gpuweb.github.io/gpuweb/# text: front-facing; url: front-facing text: shader-output mask; url: shader-output-mask text: framebuffer; url: framebuffer + text: immediateSize; url: dom-gpupipelinelayoutdescriptor-immediatesize text: normalized device coordinates; url: ndc text: clip space coordinates; url: clip-space-coordinates text: clip position; url: clip-position @@ -268,6 +269,7 @@ spec: WebGPU; urlPrefix: https://gpuweb.github.io/gpuweb/# text: rasterizationpoint-depth; url: rasterizationpoint-depth text: rasterizationpoint-perspectivedivisor; url: rasterizationpoint-perspectivedivisor text: fragmentdestination-position; url: fragmentdestination-position + text: setImmediates; url: dom-gpubindingcommandsmixin-setimmediates text: subgroups-feature; url: subgroups text: subgroupMinSize; url: dom-gpuadapterinfo-subgroupminsize text: subgroupMaxSize; url: dom-gpuadapterinfo-subgroupmaxsize @@ -5221,6 +5223,9 @@ noexport>immediate data variable. Its [=store type=] [=shader-creation error|must=] be a [=host-shareable=] [=constructible=] type, excluding arrays and structures that contain array members. Each [=entry point=] [=shader-creation error|must=] [=statically access=] at most one immediate data variable. +The value of an immediate variable is set via [=setImmediates=] commands recorded by the WebGPU API command encoder, +and remains constant during shader execution. +The variable size is limited by the pipeline layout's [=immediateSize=] configuration.
The [=AccessibleSlots=] exclude padding bytes between structure members and at the end of structures. @@ -5242,7 +5247,7 @@ It is declared at [=module scope=], exists in the [=address spaces/handle=] addr and is immutable. As described in [[#resource-interface]], uniform buffers, storage buffers, -textures, samplers, and immediate data form the [=resource interface of a shader=]. +textures, and samplers form the [=resource interface of a shader=]. The lifetime of a variable is the period during shader execution for which the [=memory locations=] are associated with the variable. @@ -9412,6 +9417,7 @@ The interface includes: * [=Shader stage inputs=] * [=Shader stage outputs=] * [=Override-declarations=] +* [=Immediate data=] * Attached [=resources=], which include: * [=Uniform buffers=] * [=Storage buffers=] @@ -9445,6 +9451,8 @@ We can now precisely define the interface of a shader as con [=statically accessed=] by the shader. - The [=override-declarations=] [=statically accessed=] by the shader. + - The [=immediate data=] variables + [=statically accessed=] by the shader. ### Inter-stage Input and Output Interface ### {#stage-inputs-outputs} @@ -10339,30 +10347,28 @@ If not, a [=pipeline-creation error=] [=behavioral requirement|will=] result. ### Resource Interface ### {#resource-interface} A resource is an object which provides access to data external to a [=shader stage=], -and which is not an [=override-declaration=] and not a [[#stage-inputs-outputs|shader stage input or output]]. +and which is not an [=override-declaration=], not an [=immediate data=] variable, and not a [[#stage-inputs-outputs|shader stage input or output]]. Resources are shared by all invocations of the shader. -There are five kinds of resources: +There are four kinds of resources: * [=Uniform buffers=] * [=Storage buffers=] * [=Texture resources=] * [=Sampler resources=] -* [=Immediate data=] The resource interface of a shader is the set of module-scope resource variables [=statically accessed=] by [=functions in a shader stage|functions in the shader stage=]. -Each resource variable, except for [=immediate data=] variables, [=shader-creation error|must=] be declared with both [=attribute/group=] and [=attribute/binding=] +Each resource variable [=shader-creation error|must=] be declared with both [=attribute/group=] and [=attribute/binding=] attributes. Together with the shader's stage, these identify the binding address of the resource on the shader's pipeline. See [[WebGPU#gpupipelinelayout]]. [=Immediate data=] variables do not use [=attribute/group=] or [=attribute/binding=] attributes. -They are accessed via the WebGPU API on the command encoder, -and are limited in size by the pipeline layout's immediate data size configuration. + Two different resource variables in a [=shader=] [=shader-creation error|must not=] have the same [=attribute/group=] and [=attribute/binding=] values, when @@ -10424,8 +10430,6 @@ where compatibility is defined by the following table. [=type/external texture=] {{GPUBindGroupLayoutEntry/externalTexture}} (not applicable) - [=immediate data=] - Not a bind group entry. Validated against pipeline layout immediate data size. See the [$validating GPUProgrammableStage|WebGPU API$] @@ -10717,8 +10721,6 @@ and |A| is a host-shareable or fixed footprint array or runtime-sized array: * AccessibleBytes(|T|) is the set of byte offsets within an instance of type |T| that contain data. * If |T| is a scalar or vector, then [=AccessibleBytes=](|T|) is the set of integers `k` such that `0 <= k <` [=SizeOf=](|T|). * If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(vec|R|) }` for `i` in `0..C-1`, where `Stride` is [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|)). - * If |T| is an atomic type `atomic`, then [=AccessibleBytes=](|T|) is [=AccessibleBytes=](|T'|). - * If |T| is an array of |N| elements of type |E|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(E) }` for `i` in `0..N-1`, where `Stride` is [=StrideOf=](|T|). * If |T| is a structure |S|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + Offset | k in AccessibleBytes(M_i) }` for each member `M_i` at offset `Offset` = [=OffsetOfMember=](|S|, `i`). * AccessibleSlots(|T|) is the set of integers `i` such that the intersection of the interval `[4*i, 4*i+4)` and [=AccessibleBytes=](|T|) is not empty. From e68f1c117216cb22022722a947449231fca5d71b Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 10 Dec 2025 18:14:49 -0500 Subject: [PATCH 13/21] Simplify description of max immediate variable size --- wgsl/index.bs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wgsl/index.bs b/wgsl/index.bs index 392a84a7ee..b70d90e74a 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -945,9 +945,7 @@ shader that goes beyond the specified limits. This maps the WebGPU [=supported limits/maxComputeWorkgroupStorageSize=] limit into a standalone WGSL limit. [=supported limits/maxComputeWorkgroupStorageSize|16384=] - Maximum combined [=byte-size=] of all [=variables=] instantiated in the - [=address spaces/immediate=] address space that are [=statically accessed=] by a single - [=shader=] + Maximum [=byte-size=] of an [=immediate data=] [=variable=]. This maps the WebGPU [=supported limits/maxImmediateSize=] limit into a standalone WGSL limit. From 3fa0d5cff91dcde01292a109dce34c0079d29a27 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Fri, 20 Mar 2026 10:22:50 +0800 Subject: [PATCH 14/21] Address comments --- spec/index.bs | 35 ++++++++++++++++++----------------- wgsl/index.bs | 12 +++++++++--- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 70c5a0cc74..8d1dafae6e 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -159,6 +159,7 @@ spec: WGSL; urlPrefix: https://gpuweb.github.io/gpuweb/wgsl/# text: subgroup size; url: subgroup-size type: abstract-op text: SizeOf; url: sizeof + text: AccessibleBytes; url: accessible-bytes text: AccessibleSlots; url: accessible-slots spec: Internationalization Glossary; urlPrefix: https://www.w3.org/TR/i18n-glossary/# type: dfn @@ -11290,7 +11291,7 @@ interface mixin GPUBindingCommandsMixin { GPUSize32 dynamicOffsetsDataLength); undefined setImmediates(GPUSize32 rangeOffset, AllowSharedBufferSource data, - optional GPUSize64 dataOffset = 0, optional GPUSize64 size); + optional GPUSize64 dataOffset = 0, optional GPUSize64 dataSize); }; @@ -11309,17 +11310,17 @@ It must only be included by interfaces which also include those mixins. :: The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry. - : \[[immediate_data]], of type [=byte sequence=], initially empty + : \[[immediate_data]], of type [=byte sequence=] :: The current immediate data bytes. - The length equals the device's {{supported limits/maxImmediateSize}}. + Initially a [=byte sequence=] of length equal to the device's {{supported limits/maxImmediateSize}}, with all bytes set to zero. Values are set by {{GPUBindingCommandsMixin/setImmediates()}}. - : \[[immediate_data_set]], of type [=list=]<{{boolean}}>, initially empty + : \[[immediate_data_set]], of type [=list=]<{{boolean}}> :: Tracks which 32-bit word slots of immediate data have been set. - The length equals the device's {{supported limits/maxImmediateSize}} divided by 4. - Each entry corresponds to a 4-byte slot and is initially `false`. + Initially a [=list=] of length equal to the device's {{supported limits/maxImmediateSize}} divided by 4, with all entries set to `false`. + Each entry corresponds to a 4-byte slot.
## Bind Groups ## {#programmable-passes-bind-groups} @@ -11703,7 +11704,7 @@ It must only be included by interfaces which also include those mixins. ## Immediate Data ## {#programmable-passes-immediate-data}
- : setImmediates(rangeOffset, data, dataOffset, size) + : setImmediates(rangeOffset, data, dataOffset, dataSize) :: Sets immediate data for subsequent render or compute commands. @@ -11713,13 +11714,13 @@ It must only be included by interfaces which also include those mixins. **Arguments:** -
+                
                     |rangeOffset|: Offset in bytes into the immediate data range to begin writing at.
                         Must be a multiple of 4.
                     |data|: Data to write into the immediate data range.
                     |dataOffset|: Offset into |data| to begin writing from. Given in elements if
                         |data| is a {{TypedArray}} and bytes otherwise. Defaults to 0.
-                    |size|: Size of content to write from |data|. Given in elements if
+                    |dataSize|: Size of content to write from |data|. Given in elements if
                         |data| is a {{TypedArray}} and bytes otherwise.
                 
@@ -11729,16 +11730,16 @@ It must only be included by interfaces which also include those mixins. 1. If |data| is an {{ArrayBuffer}} or {{DataView}}, let the element type be "byte". Otherwise, |data| is a TypedArray; let the element type be the type of the TypedArray. - 1. Let |dataSize| be the size of |data|, in elements. - 1. If |size| is missing, - let |contentsSize| be |dataSize| − |dataOffset|. - Otherwise, let |contentsSize| be |size|. + 1. Let |dataElementCount| be the size of |data|, in elements. + 1. If |dataSize| is missing, + let |contentsSize| be |dataElementCount| − |dataOffset|. + Otherwise, let |contentsSize| be |dataSize|. 1. If any of the following conditions are unsatisfied, throw an {{OperationError}} and return.
- |contentsSize| ≥ 0. - - |dataOffset| + |contentsSize| ≤ |dataSize|. + - |dataOffset| + |contentsSize| ≤ |dataElementCount|. - |contentsSize|, converted to bytes, is a multiple of 4.
1. Let |dataContents| be [=get a copy of the buffer source|a copy of the bytes held by the buffer source=] |data|. @@ -13873,8 +13874,8 @@ attachments used by this encoder. this render pass. When a {{GPURenderBundle}} is executed, it does not inherit the render pass's pipeline, bind - groups, or vertex and index buffers. After a {{GPURenderBundle}} has executed, the render - pass's pipeline, bind group, and vertex/index buffer state is cleared + groups, immediate data, or vertex and index buffers. After a {{GPURenderBundle}} has executed, the render + pass's pipeline, bind group, immediate data, and vertex/index buffer state is cleared (to the initial, empty values). Note: The state is cleared, not restored to the previous state. @@ -13939,7 +13940,7 @@ attachments used by this encoder. the following [=device timeline=] steps: 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}. - 1. Clear |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}. + 1. Set each entry of |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}} to `false`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`. 1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}. diff --git a/wgsl/index.bs b/wgsl/index.bs index b70d90e74a..74e97765f6 100644 --- a/wgsl/index.bs +++ b/wgsl/index.bs @@ -10643,7 +10643,6 @@ table. Invocations in the same [=shader stage=] [=access/read=] For [=immediate data=] variables.
- [=constructible=] [=host-shareable=] types, excluding arrays and structures containing array members.
Each [=entry point=] can [=statically access=] at most one immediate variable. handle Invocations in the same shader stage @@ -10718,8 +10717,15 @@ and |A| is a host-shareable or fixed footprint array or runtime-sized array:

* AccessibleBytes(|T|) is the set of byte offsets within an instance of type |T| that contain data. * If |T| is a scalar or vector, then [=AccessibleBytes=](|T|) is the set of integers `k` such that `0 <= k <` [=SizeOf=](|T|). - * If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the union of sets `{ k + i * Stride | k in AccessibleBytes(vec|R|) }` for `i` in `0..C-1`, where `Stride` is [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|)). - * If |T| is a structure |S|, then [=AccessibleBytes=](|T|) is the union of sets `{ k + Offset | k in AccessibleBytes(M_i) }` for each member `M_i` at offset `Offset` = [=OffsetOfMember=](|S|, `i`). + * If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the set calculated as: + * Let `Stride` be [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|)). + * For each `i` in `0..C-1`: + * For each `k` in [=AccessibleBytes=](vec|R|): + * The set contains `k + i * Stride`. + * If |T| is a structure |S|, then [=AccessibleBytes=](|T|) is the set calculated as: + * For each member `M_i` at byte offset `Offset` = [=OffsetOfMember=](|S|, `i`): + * For each `k` in [=AccessibleBytes=](`M_i`): + * The set contains `k + Offset`. * AccessibleSlots(|T|) is the set of integers `i` such that the intersection of the interval `[4*i, 4*i+4)` and [=AccessibleBytes=](|T|) is not empty. ### Alignment and Size ### {#alignment-and-size} From b16cc9e07f58d46d15655b42b8a80cf935338d4e Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Mon, 23 Mar 2026 10:58:48 +0800 Subject: [PATCH 15/21] Address comments in spec/index.bs. --- spec/index.bs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 8d1dafae6e..57307e9fed 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -159,7 +159,6 @@ spec: WGSL; urlPrefix: https://gpuweb.github.io/gpuweb/wgsl/# text: subgroup size; url: subgroup-size type: abstract-op text: SizeOf; url: sizeof - text: AccessibleBytes; url: accessible-bytes text: AccessibleSlots; url: accessible-slots spec: Internationalization Glossary; urlPrefix: https://www.w3.org/TR/i18n-glossary/# type: dfn @@ -7718,6 +7717,7 @@ To create a default pipeline layout for {{GPUPipelineBase run the following [=device timeline=] steps: 1. Let |groupCount| be 0. + 1. Let |immediateSize| be 0. 1. Let |groupDescs| be a sequence of |device|.{{device/[[limits]]}}.{{supported limits/maxBindGroups}} new {{GPUBindGroupLayoutDescriptor}} objects. 1. For each |groupDesc| in |groupDescs|: @@ -7850,6 +7850,10 @@ run the following [=device timeline=] steps: 1. Append |entry| to |groupDescs|[|group|]. + 1. For each immediate data variable that is [=statically used=] by |entryPoint|: + 1. Let |T| be the [=store type=] of the immediate data variable. + 1. Set |immediateSize| to max(|immediateSize|, [=roundUp=](4, [$SizeOf$](|T|))). + 1. Let |groupLayouts| be a new [=list=]. 1. For each |i| from 0 to |groupCount| - 1, inclusive: 1. Let |groupDesc| be |groupDescs|[|i|]. @@ -7859,6 +7863,7 @@ run the following [=device timeline=] steps: 1. Let |desc| be a new {{GPUPipelineLayoutDescriptor}}. 1. Set |desc|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}} to |groupLayouts|. + 1. Set |desc|.{{GPUPipelineLayoutDescriptor/immediateSize}} to |immediateSize|. 1. Return |device|.{{GPUDevice/createPipelineLayout()}}(|desc|). @@ -8040,6 +8045,10 @@ typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, - If the pipeline-overridable constant identified by |key| [=pipeline-overridable constant default value|does not have a default value=], |descriptor|.{{GPUProgrammableStage/constants}} |must| [=map/contain=] |key|. + 1. For each immediate data variable that is [=statically used=] by |entryPoint|: + - Let |T| be the [=store type=] of the immediate data variable. + - Let |requiredSize| be [$SizeOf$](|T|). + - |layout|.{{GPUPipelineLayout/[[immediateSize]]}} must be ≥ |requiredSize|. 1. [=pipeline-creation error|Pipeline-creation=] [=program errors=] |must| not result from the rules of the [[WGSL]] specification. 1. @@ -11634,8 +11643,6 @@ It must only be included by interfaces which also include those mixins. - |pipeline| must not be `null`. - For each immediate data variable that is [=statically used=] by any entry point in |pipeline|: - Let |T| be the [=store type=] of the immediate data variable. - - Let |requiredSize| be [$SizeOf$](|T|). - - |immediateSize| must be ≥ |requiredSize|. - Let |accessibleSlots| be [$AccessibleSlots$](|T|) as defined in [[WGSL]]. - For each |slotIndex| in |accessibleSlots|: - |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}[|slotIndex|] must be `true`. @@ -11716,10 +11723,9 @@ It must only be included by interfaces which also include those mixins.
                     |rangeOffset|: Offset in bytes into the immediate data range to begin writing at.
-                        Must be a multiple of 4.
                     |data|: Data to write into the immediate data range.
                     |dataOffset|: Offset into |data| to begin writing from. Given in elements if
-                        |data| is a {{TypedArray}} and bytes otherwise. Defaults to 0.
+                        |data| is a {{TypedArray}} and bytes otherwise.
                     |dataSize|: Size of content to write from |data|. Given in elements if
                         |data| is a {{TypedArray}} and bytes otherwise.
                 
From 8c2b20474394b9b541158f574c2c869826d65610 Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Wed, 25 Mar 2026 10:58:14 +0800 Subject: [PATCH 16/21] Update spec/index.bs Co-authored-by: Kai Ninomiya --- spec/index.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/index.bs b/spec/index.bs index 57307e9fed..21e8fdad2c 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -6963,7 +6963,8 @@ GPUPipelineLayout includes GPUObjectBase; : \[[immediateSize]], of type {{GPUSize32}}, readonly :: - The size in bytes of the immediate data range, provided at creation in {{GPUPipelineLayoutDescriptor/immediateSize|GPUPipelineLayoutDescriptor.immediateSize}}. + The size in bytes of the immediate data range, provided at creation in {{GPUPipelineLayoutDescriptor/immediateSize|GPUPipelineLayoutDescriptor.immediateSize}} + (or by the [$default pipeline layout$]).
Note: using the same {{GPUPipelineLayout}} for many {{GPURenderPipeline}} or {{GPUComputePipeline}} pipelines guarantees that the user agent doesn't need to rebind any resources internally when there is a switch between these pipelines. From 589c87b62a5029eafe39b2dc3de5cb2dd9dcf085 Mon Sep 17 00:00:00 2001 From: shaoboyan091 Date: Wed, 8 Apr 2026 10:44:50 +0800 Subject: [PATCH 17/21] Apply suggestions from code review Co-authored-by: Kai Ninomiya --- spec/index.bs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 21e8fdad2c..1b7295c8a5 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -7011,7 +7011,6 @@ pipeline, and have the following members: : immediateSize :: The size, in bytes, of the immediate data range used by the pipeline. - Must be a multiple of 4 and must not exceed {{supported limits/maxImmediateSize}}.
@@ -7851,7 +7850,8 @@ run the following [=device timeline=] steps: 1. Append |entry| to |groupDescs|[|group|]. - 1. For each immediate data variable that is [=statically used=] by |entryPoint|: + 1. If there is an immediate data variable that is [=statically used=] by |entryPoint| + ([=assert=] there is at most one): 1. Let |T| be the [=store type=] of the immediate data variable. 1. Set |immediateSize| to max(|immediateSize|, [=roundUp=](4, [$SizeOf$](|T|))). @@ -8046,7 +8046,8 @@ typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, - If the pipeline-overridable constant identified by |key| [=pipeline-overridable constant default value|does not have a default value=], |descriptor|.{{GPUProgrammableStage/constants}} |must| [=map/contain=] |key|. - 1. For each immediate data variable that is [=statically used=] by |entryPoint|: + 1. If there is an immediate data variable that is [=statically used=] by |entryPoint| + ([=assert=] there is at most one): - Let |T| be the [=store type=] of the immediate data variable. - Let |requiredSize| be [$SizeOf$](|T|). - |layout|.{{GPUPipelineLayout/[[immediateSize]]}} must be ≥ |requiredSize|. From 87585b1b85e11bbfce556f59058c48773b4b9fe9 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Wed, 8 Apr 2026 10:58:13 +0800 Subject: [PATCH 18/21] Address comments --- spec/index.bs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 1b7295c8a5..56324dcfb4 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -1630,7 +1630,6 @@ A supported limits object has a value for every limit defined by {{GPUSize32}} [=limit class/maximum=] 64 The maximum size, in bytes, of immediate data range used in a pipeline. - Must be a multiple of 4. Note: 64 bytes is the size of a 4×4 matrix of f32 values. @@ -11327,7 +11326,7 @@ It must only be included by interfaces which also include those mixins. Initially a [=byte sequence=] of length equal to the device's {{supported limits/maxImmediateSize}}, with all bytes set to zero. Values are set by {{GPUBindingCommandsMixin/setImmediates()}}. - : \[[immediate_data_set]], of type [=list=]<{{boolean}}> + : \[[immediate_slots_set ]], of type [=list=]<{{boolean}}> :: Tracks which 32-bit word slots of immediate data have been set. Initially a [=list=] of length equal to the device's {{supported limits/maxImmediateSize}} divided by 4, with all entries set to `false`. @@ -11647,7 +11646,7 @@ It must only be included by interfaces which also include those mixins. - Let |T| be the [=store type=] of the immediate data variable. - Let |accessibleSlots| be [$AccessibleSlots$](|T|) as defined in [[WGSL]]. - For each |slotIndex| in |accessibleSlots|: - - |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}[|slotIndex|] must be `true`. + - |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set ]]}}[|slotIndex|] must be `true`. Otherwise return `true`. @@ -11770,7 +11769,7 @@ It must only be included by interfaces which also include those mixins. 1. Let |byteIndex| be |rangeOffset| + |i|. 1. Let |slotIndex| be floor(|byteIndex| ÷ 4). 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|byteIndex|] to |b|. - 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}}[|slotIndex|] to true. + 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_slots_set ]]}}[|slotIndex|] to true.
@@ -12068,7 +12067,7 @@ dictionary GPUComputePassDescriptor - all of |workgroupCountX|, |workgroupCountY| and |workgroupCountZ| are ≤ |this|.device.limits.{{supported limits/maxComputeWorkgroupsPerDimension}}. - let |workgroupSize| be the computed workgroup size for - |bindingState|.{{GPUComputePassEncoder/[[pipeline]]}}. + |bindingState|.{{GPUComputePassEncoder/[[pipeline]]}}. - the entry point uses the [=builtin/workgroup_index=] built-in value and |workgroupCountX| × |workgroupCountY| × |workgroupCountZ| @@ -13948,7 +13947,7 @@ attachments used by this encoder. the following [=device timeline=] steps: 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}. - 1. Set each entry of |encoder|.{{GPUBindingCommandsMixin/[[immediate_data_set]]}} to `false`. + 1. Set each entry of |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set ]]}} to `false`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`. 1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}. From fbde276ff80cc58452f533520b0e90b7418cf5d8 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Wed, 8 Apr 2026 11:20:02 +0800 Subject: [PATCH 19/21] Move maxImmediateSize alignment requirement to adapter-capability-guarantees sections --- spec/index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/index.bs b/spec/index.bs index 56324dcfb4..0666feab2c 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -2605,6 +2605,7 @@ Any {{GPUAdapter}} returned by {{GPU/requestAdapter()}} must provide the followi - {{supported limits/maxComputeWorkgroupSizeZ}} must be ≤ {{supported limits/maxComputeInvocationsPerWorkgroup}}. - {{supported limits/maxComputeInvocationsPerWorkgroup}} must be ≤ {{supported limits/maxComputeWorkgroupSizeX}} × {{supported limits/maxComputeWorkgroupSizeY}} × {{supported limits/maxComputeWorkgroupSizeZ}}. +- {{supported limits/maxImmediateSize}} must be a multiple of 4 bytes. ### Adapter Selection ### {#adapter-selection} From 7cc2469271bd7a7768655b0392613eb61c91903e Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Thu, 7 May 2026 10:03:48 +0800 Subject: [PATCH 20/21] Address review comments on immediate data validation - Remove extra space in [[immediate_slots_set]] dfn name - Move pipeline layout/size lookups inside the validusage block to match the pattern in Validate encoder bind groups, so the null check on pipeline appears before its layout is dereferenced --- spec/index.bs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 0666feab2c..6f0d59fc31 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -11327,7 +11327,7 @@ It must only be included by interfaces which also include those mixins. Initially a [=byte sequence=] of length equal to the device's {{supported limits/maxImmediateSize}}, with all bytes set to zero. Values are set by {{GPUBindingCommandsMixin/setImmediates()}}. - : \[[immediate_slots_set ]], of type [=list=]<{{boolean}}> + : \[[immediate_slots_set]], of type [=list=]<{{boolean}}> :: Tracks which 32-bit word slots of immediate data have been set. Initially a [=list=] of length equal to the device's {{supported limits/maxImmediateSize}} divided by 4, with all entries set to `false`. @@ -11636,18 +11636,18 @@ It must only be included by interfaces which also include those mixins. [=Device timeline=] steps: - 1. Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. - 2. Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. - 3. If |immediateSize| is 0, return `true`. - 4. If any of the following conditions are unsatisfied, return `false`: + 1. If any of the following conditions are unsatisfied, return `false`:
+ - Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. - |pipeline| must not be `null`. + - Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. + - If |immediateSize| is 0, return `true`. - For each immediate data variable that is [=statically used=] by any entry point in |pipeline|: - Let |T| be the [=store type=] of the immediate data variable. - Let |accessibleSlots| be [$AccessibleSlots$](|T|) as defined in [[WGSL]]. - For each |slotIndex| in |accessibleSlots|: - - |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set ]]}}[|slotIndex|] must be `true`. + - |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set]]}}[|slotIndex|] must be `true`.
Otherwise return `true`. @@ -11770,7 +11770,7 @@ It must only be included by interfaces which also include those mixins. 1. Let |byteIndex| be |rangeOffset| + |i|. 1. Let |slotIndex| be floor(|byteIndex| ÷ 4). 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_data]]}}[|byteIndex|] to |b|. - 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_slots_set ]]}}[|slotIndex|] to true. + 1. Set |this|.{{GPUBindingCommandsMixin/[[immediate_slots_set]]}}[|slotIndex|] to true. @@ -13948,7 +13948,7 @@ attachments used by this encoder. the following [=device timeline=] steps: 1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}. - 1. Set each entry of |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set ]]}} to `false`. + 1. Set each entry of |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set]]}} to `false`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`. 1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`. 1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}. From f2fc3f3df477bd4cd17688f8c0fefdb3411e7a7f Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 7 May 2026 15:21:51 +0200 Subject: [PATCH 21/21] editorial: Validate immediate data --- spec/index.bs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 6f0d59fc31..8081fd61f9 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -11639,10 +11639,7 @@ It must only be included by interfaces which also include those mixins. 1. If any of the following conditions are unsatisfied, return `false`:
- - Let |pipelineLayout| be |pipeline|.{{GPUPipelineBase/[[layout]]}}. - |pipeline| must not be `null`. - - Let |immediateSize| be |pipelineLayout|.{{GPUPipelineLayout/[[immediateSize]]}}. - - If |immediateSize| is 0, return `true`. - For each immediate data variable that is [=statically used=] by any entry point in |pipeline|: - Let |T| be the [=store type=] of the immediate data variable. - Let |accessibleSlots| be [$AccessibleSlots$](|T|) as defined in [[WGSL]]. @@ -11650,7 +11647,7 @@ It must only be included by interfaces which also include those mixins. - |encoder|.{{GPUBindingCommandsMixin/[[immediate_slots_set]]}}[|slotIndex|] must be `true`.
- Otherwise return `true`. + Otherwise return `true`.