- Notifications
You must be signed in to change notification settings - Fork353
Specify immediate data API and WGSL <immediate> address space#5423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Pull Request Overview
This PR adds support for immediate data in WebGPU pipelines, allowing small amounts of data to be passed directly to shaders without requiring buffer bindings. This is useful for frequently changing small data like transformation matrices.
- Introduces
maxImmediateSizelimit (64 bytes) for immediate data ranges - Adds
immediateSizeparameter to pipeline layouts and validation for immediate data usage - Implements
setImmediateData()method for setting immediate data in encoders
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
github-actionsbot commentedOct 29, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Previews, as seen when thisbuild job started (6cc7322): |
jimblandy commentedOct 29, 2025
minutes from API committee meeting 2025-10-29
|
spec/index.bs Outdated
| :: | ||
| The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry. | ||
| : <dfn>\[[immediate_data]]</dfn>, of type [=ordered map=]<{{GPUSize32}}, [=list=]<[=byte=]>>, initially empty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
nit: The array of arrays of bytes is a little confusing to read, I think it would be easier to describe if we tracked the data and the initialization-state separately. Data as an array of N bytes, initialization state as an array of N/4 booleans.
| - |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|: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
When I read this I think "immediate data variable" means onevar<immediate>, of which there is of course only one (at most). But thatvar itself has aSizeOf the entire struct, whereas we actually want onlyaccessible parts of the struct to need to be initialized. That is going to require the WGSL spec to "reflect" some new information for us - the map ofslot -> is_accessible - for us to validate against.
I would recommend combining this PR with the WGSL PR, since there will be new integration between the specs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Ok, Let me do this and abandon wgsl PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Unfortunately the WGSL spec doesn't really defined "padding" or "contains actual data". It probably should, but that's probably too much to ask to fix in this PR.
I think the text in this PR is good enough for all but the most adversarial reader. I recommend a followup PR (by the WGSL editors) to more carefully define padding and non-padding (or whatever we cal it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
API spec still needs to be updated to useAccessibleBytes?
wgsl/index.bs Outdated
| <td>Invocations in the same [=shader stage=] | ||
| <td>[=access/read=] | ||
| <td>For [=immediate data=] variables.<br> | ||
| [=type/concrete|Concrete=] [=constructible=] [=host-shareable=] types, excluding arrays and structures containing array members.<br> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Copying Alan's comment here:
#5424 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I agree with Alan here, the "Concrete ..." part is redundant, and should be removed.
alan-baker left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Copying my other requests from#5424
There ought to be either a language feature or enable extension as part of this specification.
Additionally, there need to be some updates to the Resource Interface and Resource Layout Compatibility sections in the Entry Points chapter.
Uh oh!
There was an error while loading.Please reload this page.
spec/index.bs Outdated
| GPUSize64 dynamicOffsetsDataStart, | ||
| GPUSize32 dynamicOffsetsDataLength); | ||
| undefined setImmediateData(GPUSize32 rangeOffset, AllowSharedBufferSource data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We didn't really discuss this in the group, but do you have any opinion aboutsetImmediates instead? This is to try to improve the ergonomics very slightly but might be a bit confusing to developers given that an ArrayBuffer is passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'm Ok forsetImmediates (TBH, I'm Ok for all simplification names). But better go through community meeting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
setImmediates sounds good 👍
dneto0 left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I agree with other review comments. Once those are resolved I'm happy with this landing.
wgsl/index.bs Outdated
| <td>Invocations in the same [=shader stage=] | ||
| <td>[=access/read=] | ||
| <td>For [=immediate data=] variables.<br> | ||
| [=type/concrete|Concrete=] [=constructible=] [=host-shareable=] types, excluding arrays and structures containing array members.<br> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I agree with Alan here, the "Concrete ..." part is redundant, and should be removed.
| - |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|: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Unfortunately the WGSL spec doesn't really defined "padding" or "contains actual data". It probably should, but that's probably too much to ask to fix in this PR.
I think the text in this PR is good enough for all but the most adversarial reader. I recommend a followup PR (by the WGSL editors) to more carefully define padding and non-padding (or whatever we cal it).
mwyrzykowski commentedNov 4, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Would prefer not using an enable extension since this is supported or can be easily emulated by all core devices (and compat too I think). I.e., WGSL language extension would be my preference. |
mwyrzykowski commentedNov 5, 2025
I can't make the meeting today but this is 👍 from Apple's side. Metal doesn't have the concept of immediates and there was some discussion about this taking up a buffer slot. If the UA is using argument buffers then it would not, since a slot is already needed for dynamic data and you can reuse / share the same slot for immediates. Basically any transient data (dynamic offsets, immediates, other constants) can be passed in a single call to set[Vertex/Fragment]Bytes. But we are also fine with this taking up a slot. This allows UAs to not have to migrate to ABs for everything for their Metal backends. I suppose its a tradeoff but either way is 👍 from Apple's side given our AB implementation is not really impacted either way. |
jimblandy commentedNov 6, 2025
minutes from WGSL committee meeting 2025-11-4
|
Uh oh!
There was an error while loading.Please reload this page.
wgsl/index.bs Outdated
| The set of accessible slots is computed as follows: | ||
| <blockquote algorithm="accessible slots"> | ||
| <dfn export>AccessibleSlots</dfn>(|T|) computes a [=set=] of slot indices (where each slot is 4 bytes): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
As far as I can tell there could be nested structs. If we want to go with this algorithm, maybe we should instead recursively find all scalar and vector types and require the range [offset, offset + size) be set in the API?
As it stands for the structure cases I could have:
struct S { a : vec4u, b : u32}struct T { x : S, y : u32,}var<immediate> imm : T;Since the align ofS is 16,SizeOf(S) is 32. That means some padding bytes are required to be set.
The spec uses, but does not define padding bytes. I wonder if it would be better to just define padding bytes and use that here.
Kangz commentedNov 12, 2025
@amaiorano is implementing this speculatively in Dawn, we are going with |
alan-baker commentedNov 12, 2025
That works, but I'd also be ok with just |
Uh oh!
There was an error while loading.Please reload this page.
shaoboyan091 commentedDec 1, 2025
Rebase and iterate the PR. The new changes:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card.Take the survey.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card.Take the survey.
| : <dfn>\[[immediate_data]]</dfn>, of type [=byte sequence=], initially empty | ||
| :: | ||
| The current immediate data bytes. | ||
| The length equals the device's {{supported limits/maxImmediateSize}}. | ||
| Values are set by {{GPUBindingCommandsMixin/setImmediates()}}. |
CopilotAIDec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The[[immediate_data]] internal slot is described as "initially empty" but also states "The length equals the device's {{supported limits/maxImmediateSize}}." These two statements are contradictory. It should be initialized to a byte sequence of lengthmaxImmediateSize with all bytes set to a default value (e.g., 0), not "initially empty".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
+1
| : <dfn>\[[immediate_data_set]]</dfn>, 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`. |
CopilotAIDec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The[[immediate_data_set]] internal slot is described as "initially empty" but also states "The length equals the device's {{supported limits/maxImmediateSize}} divided by 4." These two statements are contradictory. It should be initialized to a list of lengthmaxImmediateSize / 4 with all entries set tofalse, not "initially empty".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
+1
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
wgsl/index.bs Outdated
| Resources are shared by all invocations of the shader. | ||
| There arefour kinds of resources: | ||
| There arefive kinds of resources: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
These edits look inconsistent with Shader Interfaces now. Also the carve outs for immediate data aren't great with the rest of the spec. I wonder if we should instead introduce a new interface for immediates. That might make all this cleaner. Thoughts@dneto0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yes, I think so.
There is a strong association with the pipeline layout rules, and those are all about things that have binding points.
Immediates are more like overrides, and override-declarations are already separately called out in the Shader Interface (section 13.3).
Also, override-declarations are called out in footnote 4 of the table in seciton 7. Variable and value declarations.
Tomorrow I'll try to make a patch to this PR to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I've reworked it to make immediate data variables their own aspect of the shader interface, not part of the resource interface.
PTAL@alan-baker
Uh oh!
There was an error while loading.Please reload this page.
dneto0 left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This should add "immediate variable declaration, if any" to the list at the top of13.3 Shader Interface, and to the bulleted list that defines 'interface of a shader' (add a bullet similar to override -declarations)
Then I think we don't have to touch the "resource interface" section at all (i.e. no additions for immediates).
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
wgsl/index.bs Outdated
| Resources are shared by all invocations of the shader. | ||
| There arefour kinds of resources: | ||
| There arefive kinds of resources: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yes, I think so.
There is a strong association with the pipeline layout rules, and those are all about things that have binding points.
Immediates are more like overrides, and override-declarations are already separately called out in the Shader Interface (section 13.3).
Also, override-declarations are called out in footnote 4 of the table in seciton 7. Variable and value declarations.
Tomorrow I'll try to make a patch to this PR to do this.
| |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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
nit: Maybe this should be calleddataSize, so it's clearly associated with the type ofdata (not specified in bytes). We can changewriteBuffer as well.
They are their own thing in the shader interface, similar to overridedeclarations
dneto0 commentedDec 10, 2025
WGSL part LGTM now |
kainino0x commentedDec 16, 2025
Marking draft just since we said we would finish CTS before landing this. |
| * <dfn noexport id="accessible-bytes">AccessibleBytes</dfn>(|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`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
These are very hard to decypher. I think it might be better to write it out as something more then the set notation (I didn't realize the| ink + Offset | k in.. was doing set and wasn't or'ing together two things).
Maybe something like:
If |T| Is a structure |S|, then [=AccessibleBytes=](|T|) is the set calculated as: * for each member `i` in S * for each k in AccessibleBytes(`i`) * Then the set contains k + `Offset`, where `Offset` = [=OffsetOfMember](|S|, `i`)A similar reformatting formatrix could also be helpful
If |T| is a matrix with |C| columns and |R| rows, then [=AccessibleBytes=](|T|) is the set calculated as: * for each `i` in 0..C-1 * for each k in AccessibleBytes(vec|R|) * Then the set contains k + i * `Stride`, where `Stride` is [=roundUp=]([=AlignOf=](vec|R|), [=SizeOf=](vec|R|))
Landing the immediate data API specification into the WebGPU spec. Ref to proposal: immediate-data.md.