Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 140 additions & 11 deletions spec/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -2885,6 +2885,7 @@ enum GPUFeatureName {
"clip-distances",
"dual-source-blending",
"subgroups",
"interited-bundle-bind-groups",
Comment thread
Kangz marked this conversation as resolved.
};
</script>

Expand Down Expand Up @@ -10804,6 +10805,12 @@ It must only be included by interfaces which also include those mixins.
: <dfn>\[[dynamic_offsets]]</dfn>, of type [=ordered map=]&lt;{{GPUIndex32}}, [=list=]&lt;{{GPUBufferDynamicOffset}}&gt;&gt;, initally empty
::
The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could replace [[bind_groups]] with [[bind_groups_layouts]] or track both? This could avoid the need for placeholder bind groups as a concept. WDYT?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered about that myself. I'll take a pass at it and see how complicated it would be.

: <dfn>\[[inheritedBindGroupState]]</dfn>, of type [=ordered map=]&lt;{{GPUIndex32}}, [=InheritedBindGroupState=]}}&gt;, initially empty
::
State for the bind groups that will be inherited from an executing encoder.

Note: This will only have entries in {{GPURenderBundleEncoder}} instances.
</dl>

## Bind Groups ## {#programmable-passes-bind-groups}
Expand Down Expand Up @@ -10863,6 +10870,7 @@ It must only be included by interfaces which also include those mixins.
- |index| must be &lt;
|this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxBindGroups}}.
- |dynamicOffsets|.[=list/size=] must equal |dynamicOffsetCount|.
- |this|.{{GPUBindingCommandsMixin/[[inheritedBindGroupState]]}} must not [=map/contain=] |index|.
</div>
1. If |bindGroup| is `null`:
1. [=map/Remove=] |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|index|].
Expand Down Expand Up @@ -11046,6 +11054,7 @@ It must only be included by interfaces which also include those mixins.
- If |bindGroupLayout| is `null`, [=iteration/continue=].
- Let |bindGroup| be |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|index|].
- Let |dynamicOffsets| be |encoder|.{{GPUBindingCommandsMixin/[[dynamic_offsets]]}}[|index|].
- Let |inheritedState| be |encoder|.{{GPUBindingCommandsMixin/[[inheritedBindGroupState]]}}[|index|].
- |bindGroup| must not be `null`.
- |bindGroup|.{{GPUBindGroup/[[layout]]}} must be [=group-equivalent=] with |bindGroupLayout|.
- Let |dynamicOffsetIndex| be 0.
Expand All @@ -11062,8 +11071,13 @@ It must only be included by interfaces which also include those mixins.
|dynamicOffsets|[|dynamicOffsetIndex|].
- Increment |dynamicOffsetIndex| by 1.
- If |bindGroupEntry|.{{GPUBindGroupEntry/[[prevalidatedSize]]}} is `false`:
- [$effective buffer binding size$](|bound|) must be &ge; [=minimum buffer binding size=]
of the binding variable in |pipeline|'s shader that corresponds to |bindGroupEntry|.
- Let |minimumBindingSize| be the [=minimum buffer binding size=] of the binding variable in
|pipeline|'s shader that corresponds to |bindGroupEntry|.
- If |inheritedState| [=map/exists=]:
- Set |inheritedState|.{{InheritedBindGroupState/[[minimumBufferBindingSize]]}}[|bindGroupEntry|.{{GPUBindGroupEntry/binding}}]
to the maximum of |minimumBindingSize| and |inheritedState|.{{InheritedBindGroupState/[[minimumBufferBindingSize]]}}[|bindGroupEntry|.{{GPUBindGroupEntry/binding}}]
- Otherwise:
- [$effective buffer binding size$](|bound|) must be &ge; |minimumBindingSize|.
- [$Encoder bind groups alias a writable resource$](|encoder|, |pipeline|) must be `false`.
</div>

Expand Down Expand Up @@ -13185,10 +13199,11 @@ attachments used by this encoder.
Executes the commands previously recorded into the given {{GPURenderBundle}}s as part of
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
(to the initial, empty values).
When a {{GPURenderBundle}} is executed, it does not inherit the render pass's pipeline or
vertex and index buffers. Bind groups are not inherited unless explicitly indicated at
bundle creation time. See [=inherited bundle state=]. After a {{GPURenderBundle}} has
executed, the render pass's pipeline, vertex/index buffer, and non-inherited bind group
state are cleared (to the initial, empty values).

Note: The state is cleared, not restored to the previous state.
This occurs even if zero {{GPURenderBundle|GPURenderBundles}} are executed.
Expand Down Expand Up @@ -13222,6 +13237,14 @@ attachments used by this encoder.
- |this|.{{GPURenderCommandsMixin/[[layout]]}} must equal |bundle|.{{GPURenderBundle/[[layout]]}}.
- If |this|.{{GPURenderCommandsMixin/[[depthReadOnly]]}} is true, |bundle|.{{GPURenderBundle/[[depthReadOnly]]}} must be true.
- If |this|.{{GPURenderCommandsMixin/[[stencilReadOnly]]}} is true, |bundle|.{{GPURenderBundle/[[stencilReadOnly]]}} must be true.
- For each ({{GPUIndex32}} |group|, [=InheritedBindGroupState=] |inheritedState|) in |bundle|.{{GPURenderBundle/[[inheritedBindGroupState]]}}:
- Let |bindGroup| be |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|group|].
- |bindGroup|.{{GPUBindGroup/[[layout]]}} must be [=group-equivalent=] with |inheritedState|.{{InheritedBindGroupState/[[layout]]}}.
- For each ({{GPUIndex32}} |binding|, {{GPUSize64}} |minimumBindingSize|) in |inheritedState|.{{InheritedBindGroupState/[[minimumBufferBindingSize]]}}:
- Let |bindGroupEntry| be the entry in |bindGroup|.{{GPUBindGroup/[[entries]]}} where |bindGroupEntry|.{{GPUBindGroupEntry/binding}} is |binding|.
- Let |bound| be a copy of |bindGroupEntry|.{{GPUBindGroupEntry/resource}}.
- [=Assert=] |bound| is a {{GPUBufferBinding}}.
- [$effective buffer binding size$](|bound|) must be &ge; |minimumBindingSize|.
</div>

1. For each |bundle| in |bundles|:
Expand All @@ -13234,23 +13257,28 @@ attachments used by this encoder.
<div data-timeline=queue>
[=Queue timeline=] steps:

1. For each ({{GPUIndex32}} |group|, [=InheritedBindGroupState=] |inheritedState|) in |bundle|.{{GPURenderBundle/[[inheritedBindGroupState]]}}:
1. Substitue the [=placeholder bind group=] at index |group| used when encoding |bundle|
with |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|group|].

1. Execute each command in |bundle|.{{GPURenderBundle/[[command_list]]}}
with |renderState|.

Note: |renderState| cannot be changed by executing render bundles. Binding state was
already captured at bundle encoding time, and so isn't used when executing bundles.
</div>

1. [$Reset the render pass binding state$] of |this|.
1. [$Reset the render pass binding state$] of |this| for |bundle|.
</div>
</div>
</dl>

<div algorithm data-timeline=device>
To <dfn abstract-op>Reset the render pass binding state</dfn> of {{GPURenderPassEncoder}} |encoder| run
the following [=device timeline=] steps:
To <dfn abstract-op>Reset the render pass binding state</dfn> of {{GPURenderPassEncoder}} |encoder| for
{{GPURenderBundle}} |bundle| run the following [=device timeline=] steps:

1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}.
1. For each {{GPUBindGroup}} group at |index| in |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}.
1. If |bundle|.{{GPURenderBundle/[[inheritedBindGroupState]]}} does not [=map/contain=] |index|:
1. [=map/Remove=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|index|]
1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`.
1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`.
1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}.
Expand Down Expand Up @@ -13301,6 +13329,10 @@ GPURenderBundle includes GPUObjectBase;
: <dfn>\[[drawCount]]</dfn>, of type {{GPUSize64}}
::
The number of draw commands in this {{GPURenderBundle}}.

: <dfn>\[[inheritedBindGroupState]]</dfn>, of type [=ordered map=]&lt;{{GPUIndex32}}, [=InheritedBindGroupState=]}}&gt;, initially empty
::
State for the bind groups this render bundle will inherit from the render pass when executing.
</dl>

### Render Bundle Creation ### {#render-bundle-creation}
Expand Down Expand Up @@ -13372,12 +13404,22 @@ GPURenderBundleEncoder includes GPURenderCommandsMixin;
- A non-`null` value in
|descriptor|.{{GPURenderPassLayout/colorFormats}}, or
- A |descriptor|.{{GPURenderPassLayout/depthStencilFormat}}.
- If |descriptor|.{{GPURenderBundleEncoderDescriptor/inheritedBindGroups}} is not [=list/is empty|empty=]:
- {{GPUFeatureName/"interited-bundle-bind-groups"}} must be [=enabled for=] |this|.
</div>
1. Set |e|.{{GPURenderCommandsMixin/[[layout]]}} to a copy of |descriptor|'s included {{GPURenderPassLayout}} interface.
1. Set |e|.{{GPURenderCommandsMixin/[[depthReadOnly]]}} to |descriptor|.{{GPURenderBundleEncoderDescriptor/depthReadOnly}}.
1. Set |e|.{{GPURenderCommandsMixin/[[stencilReadOnly]]}} to |descriptor|.{{GPURenderBundleEncoderDescriptor/stencilReadOnly}}.
1. Set |e|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/open=]".
1. Set |e|.{{GPURenderCommandsMixin/[[drawCount]]}} to 0.
1. For each {{GPURenderBundleInheritedBindGroupEntry}} |inheritedBindGroup| in
|descriptor|.{{GPURenderBundleEncoderDescriptor/inheritedBindGroups}}:
1. Let |group| be |inheritedBindGroup|.{{GPURenderBundleInheritedBindGroupEntry/group}}.
1. Let |layout| be |inheritedBindGroup|.{{GPURenderBundleInheritedBindGroupEntry/layout}}.
1. Let |inheritedState| be a new [=InheritedBindGroupState=] object.
1. Set |inheritedState|.{{InheritedBindGroupState/[[layout]]}} to |layout|.
1. Set |e|.{{GPUBindingCommandsMixin/[[inheritedBindGroupState]]}}[|group|] to be |inheritedState|.
1. Set |e|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|group|] to be a new [=placeholder bind group=] that satisfies |layout|.
</div>
</div>
</dl>
Expand All @@ -13389,6 +13431,9 @@ dictionary GPURenderBundleEncoderDescriptor
: GPURenderPassLayout {
boolean depthReadOnly = false;
boolean stencilReadOnly = false;

// Requires "interited-bundle-bind-groups" feature.
sequence<GPURenderBundleInheritedBindGroupEntry> inheritedBindGroups = [];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could match GPUPipelineLayoutDescriptor with sequece<GPUBindGroupLayout?>. This would force that the inherited bindgroups are a prefix of all the groups, that is you can inherit groups 0 , 1, 2 but not just 0, 2. This would be a useful way to encode in the API that the groups should be ordered from less frequently changed to more frequently changed.

};
</script>

Expand All @@ -13408,6 +13453,77 @@ dictionary GPURenderBundleEncoderDescriptor
in.

See [=read-only depth-stencil=].

: <dfn>inheritedBindGroups</dfn>
::
An list of entries describing which bind groups, if any, should be inherited from the
render pass the render bundle is executed in.

See [=inherited bundle state=].

Requires the {{GPUFeatureName/"interited-bundle-bind-groups"}} feature to be enabled.
</dl>

### Inherited Bundle State ### {#render-bundle-inherited-state}

By default render bundles [$Reset the render pass binding state|reset the binding state$] when
executing to ensure that the bundle will operate the same way regardless of the context in which
it's executed. Render bundles can explicitly request, however, that some bindings be
<dfn>inherited bundle state</dfn> from the executing render pass. [=Inherited bundle state=] is
defined at bundle creation time and prevents the identified bindings from being reset before and
after the bundle execution. This allows bundles to be used in more flexible situations. For example:
Inheriting a bind group containing view information to more easily render the bundle content from
multiple viewports in a single frame.

At this time only {{GPUBindGroup}}s may be inhertited from the render pass.
Comment thread
toji marked this conversation as resolved.

<script type=idl>
dictionary GPURenderBundleInheritedBindGroupEntry {
required GPUIndex32 group;
required GPUBindGroupLayout layout;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderBundleInheritedBindGroupEntry>
: <dfn>group</dfn>
::
The index of a bind group to be inherited from the render pass the render bundle is executed
in. Must be &lt; {{supported limits/maxBindGroups}}.

: <dfn>layout</dfn>
::
The layout of the {{GPUBindGroup}} that must be set at index {{GPURenderBundleInheritedBindGroupEntry/group}}
when the render bundle is executed in order for the bundle execution to be valid.
</dl>

<div class=note heading>
Render bundles that make use of inherited bind groups have the following behavior differences:

- Bind groups indices marked as inherited cannot be changed by the render bundle.
- Inherited bind groups are not [$Reset the render pass binding state|reset$]
when the render bundle is finished executing. All other bind groups continue to be reset as
usual.
</div>

A <dfn>placeholder bind group</dfn> is a {{GPUBindGroup}} that satisfies a given {{GPUBindGroupLayout}}
for purposes of validation but does not contain any actual resources. They are a conceptual mechanism
intended to simplify spec prose regarding [=inherited bundle state=].

During encoding the state of inherited bind groups is tracked using a list of internal
<dfn dfn>InheritedBindGroupState</dfn> objects.

An [=InheritedBindGroupState=] object has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=InheritedBindGroupState data-timeline=device>
: <dfn>\[[layout]]</dfn>, of type {{GPUBindGroup}}
::
The layout of the {{GPUBindGroup}} that will be inherited when the render bundle is
executed.

: <dfn>\[[minimumBufferBindingSize]]</dfn>, of type [=ordered map=]&lt;{{GPUIndex32}}, {{GPUSize64}}&gt;, initially empty.
::
The [=minimum buffer binding size=] for each buffer {{GPUBindGroupEntry/binding}}
required for that buffer to be valid for the all draw calls in the render bundle.
</dl>

### Finalization ### {#render-bundle-finalization}
Expand Down Expand Up @@ -13457,6 +13573,8 @@ dictionary GPURenderBundleEncoderDescriptor
|this|.{{GPURenderCommandsMixin/[[usage scope]]}}.
1. Set |renderBundle|.{{GPURenderBundle/[[drawCount]]}} to
|this|.{{GPURenderCommandsMixin/[[drawCount]]}}.
1. Set |renderBundle|.{{GPURenderBundle/[[inheritedBindGroupState]]}} to
|this|.{{GPUBindingCommandsMixin/[[inheritedBindGroupState]]}}
</div>
</div>
</dl>
Expand Down Expand Up @@ -16732,6 +16850,17 @@ expose real values whenever the feature is available on the adapter:
- New WGSL extensions:
- [=extension/subgroups=]

<h3 id=interited-bundle-bind-groups data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"interited-bundle-bind-groups"`
<span id=dom-gpufeaturename-interited-bundle-bind-groups></span>
</h3>

Allows {{GPUBindGroup}}s to be [=inherited bundle state=] for {{GPURenderBundle}}s.

This feature adds the following [=optional API surfaces=]:

- New {{GPURenderBundleEncoderDescriptor}} dictionary members:
- {{GPURenderBundleEncoderDescriptor/inheritedBindGroups}}

# Appendices # {#appendices}

## Texture Format Capabilities ## {#texture-format-caps}
Expand Down