Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bind group entries #9694

Merged
merged 35 commits into from
Oct 21, 2023
Merged

Bind group entries #9694

merged 35 commits into from
Oct 21, 2023

Conversation

robtfm
Copy link
Contributor

@robtfm robtfm commented Sep 4, 2023

Objective

Simplify bind group creation code. alternative to (and based on) #9476

Solution

  • Add a BindGroupEntries struct that can transparently be used where &[BindGroupEntry<'b>] is required in BindGroupDescriptors.

Allows constructing the descriptor's entries as:

render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::with_indexes((
        (2, &my_sampler),
        (3, my_uniform),
    )),
);

instead of

render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 2,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 3,
            resource: my_uniform,
        },
    ],
);

or

render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::sequential((&my_sampler, my_uniform)),
);

instead of

render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 0,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 1,
            resource: my_uniform,
        },
    ],
);

the structs has no user facing macros, is tuple-type-based so stack allocated, and has no noticeable impact on compile time.

  • Also adds a DynamicBindGroupEntries struct with a similar api that uses a Vec under the hood and allows extending the entries.
  • Modifies RenderDevice::create_bind_group to take separate arguments label, layout and entries instead of a BindGroupDescriptor struct. The struct can't be stored due to the internal references, and with only 3 members arguably does not add enough context to justify itself.
  • Modify the codebase to use the new api and the BindGroupEntries / DynamicBindGroupEntries structs where appropriate (whenever the entries slice contains more than 1 member).

Migration Guide

  • Calls to RenderDevice::create_bind_group({BindGroupDescriptor { label, layout, entries }) must be amended to RenderDevice::create_bind_group(label, layout, entries).
  • If labels have been specified as "bind_group_name".into(), they need to change to just "bind_group_name". Some("bind_group_name") and None will still work, but Some("bind_group_name") can optionally be simplified to just "bind_group_name".

@robtfm robtfm added A-Rendering Drawing game state to the screen C-Code-Quality A section of code that is hard to understand or change X-Controversial There is active debate or serious implications around merging this PR labels Sep 4, 2023
Copy link
Contributor

@IceSentry IceSentry left a comment

Choose a reason for hiding this comment

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

Alright, I went over everything it looks really good. The diffs are so nice to look at!

LGTM

render_device.create_bind_group(
Some("model_only_mesh_bind_group"),
&self.model_only,
&[entry::model(0, model.clone())],
Copy link
Contributor

Choose a reason for hiding this comment

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

It makes sense to not change this in this PR but the pattern used here and the rest of the file will be easy to remove in a future PR.

examples/shader/post_processing.rs Outdated Show resolved Hide resolved
@IceSentry
Copy link
Contributor

Oh, I reviewed before the IntoLabel but that's really nice to see too!

@IceSentry
Copy link
Contributor

I think you can remove the .as_slice() from the PR description

@JMS55 JMS55 self-requested a review September 5, 2023 01:13
@robtfm
Copy link
Contributor Author

robtfm commented Sep 5, 2023

Not sure what went wrong with the ci, I tried on my repo and it’s green. I also don’t know how to rerun it any more (rip bors)

@IceSentry
Copy link
Contributor

Yeah, build works fine on my side too. Must be a CI issue, I think only maintainers are allowed to force rerun CI. Alternatively you could push a new commit

Copy link
Contributor

@superdump superdump left a comment

Choose a reason for hiding this comment

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

Approving but do not merge until after #9258 is merged. I don't want to disrupt that large PR.

@IceSentry
Copy link
Contributor

IceSentry commented Oct 17, 2023

I've been using this in a codebase that uses UniformBuffer<T>. I added this impl to make it work for me.

impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a UniformBuffer<T> {
    #[inline]
    fn into_binding(self) -> BindingResource<'a> {
        BindingResource::Buffer(
            self.buffer()
                .expect("Failed to get buffer")
                .as_entire_buffer_binding(),
        )
    }
}

We could also add this so it can work with any buffer binding

impl<'a> IntoBinding<'a> for BufferBinding<'a> {
    #[inline]
    fn into_binding(self) -> BindingResource<'a> {
        BindingResource::Buffer(self)
    }
}

I also have used this one for DynamicUniformBuffer but the the ShaderType bound could be a bit annoying

impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicUniformBuffer<T> 
{ 
    #[inline] 
    fn into_binding(self) -> BindingResource<'a> { 
        BindingResource::Buffer(BufferBinding { 
            buffer: self.buffer().expect("Failed to get buffer"), 
            offset: 0, 
            size: Some(T::min_size()), 
        }) 
    } 
} 

@robtfm
Copy link
Contributor Author

robtfm commented Oct 17, 2023

I added your impl for UniformBuffer and wgpu::BufferBinding, thanks!

I also have used this one for DynamicUniformBuffer

this one was already in the PR though

@IceSentry
Copy link
Contributor

this one was already in the PR though

Ah, right, sorry, it was in uniform_buffer.rs. When using it I just copied the bind_group_entries.rs module so I missed that one.

bindgroup: render_device.create_bind_group(
"LineGizmoUniform bindgroup",
&line_gizmo_uniform_layout.layout,
&[BindGroupEntry {
Copy link
Contributor

Choose a reason for hiding this comment

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

With the UniformBuffer/BufferBinding impl you should be able to use it here too now I think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i didn't use it anywhere there was only 1 entry, it doesn't really save any typing. i guess i could for consistency though?

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, makes sense then. I'll personally use it everywhere in my own code but I think it's fine as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it ends up looking like &BindGroupEntries::sequential((binding,)), which is a bit weird...

Copy link
Contributor

Choose a reason for hiding this comment

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

Oof, yeah, nevermind then 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there's actually quite a few single-entry bindings at zero over the codebase ... i'll try adding a BindGroupEntries::single method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added, i think it's nice

@IceSentry
Copy link
Contributor

IceSentry commented Oct 18, 2023

Here's another impl that could be useful to have

impl<'a> IntoBinding<'a> for &'a CachedTexture {
    #[inline]
    fn into_binding(self) -> BindingResource<'a> {
        BindingResource::TextureView(&self.default_view)
    }
}

I needed that because ViewPrepassTextures holds Option<CachedTexture> and I wanted to create a bind group with them.

I wanted to suggest an impl that unwraps for Options but I think it makes sense to do unwrapping at the callsites.

@superdump superdump added this pull request to the merge queue Oct 21, 2023
Merged via the queue into bevyengine:main with commit 6f2a5cb Oct 21, 2023
21 checks passed
@IceSentry IceSentry mentioned this pull request Oct 22, 2023
3 tasks
robtfm added a commit to robtfm/bevy that referenced this pull request Oct 23, 2023
# Objective

Simplify bind group creation code. alternative to (and based on) bevyengine#9476

## Solution

- Add a `BindGroupEntries` struct that can transparently be used where
`&[BindGroupEntry<'b>]` is required in BindGroupDescriptors.

Allows constructing the descriptor's entries as:
```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::with_indexes((
        (2, &my_sampler),
        (3, my_uniform),
    )),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 2,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 3,
            resource: my_uniform,
        },
    ],
);
```

or

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::sequential((&my_sampler, my_uniform)),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 0,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 1,
            resource: my_uniform,
        },
    ],
);
```

the structs has no user facing macros, is tuple-type-based so stack
allocated, and has no noticeable impact on compile time.

- Also adds a `DynamicBindGroupEntries` struct with a similar api that
uses a `Vec` under the hood and allows extending the entries.
- Modifies `RenderDevice::create_bind_group` to take separate arguments
`label`, `layout` and `entries` instead of a `BindGroupDescriptor`
struct. The struct can't be stored due to the internal references, and
with only 3 members arguably does not add enough context to justify
itself.
- Modify the codebase to use the new api and the `BindGroupEntries` /
`DynamicBindGroupEntries` structs where appropriate (whenever the
entries slice contains more than 1 member).

## Migration Guide

- Calls to `RenderDevice::create_bind_group({BindGroupDescriptor {
label, layout, entries })` must be amended to
`RenderDevice::create_bind_group(label, layout, entries)`.
- If `label`s have been specified as `"bind_group_name".into()`, they
need to change to just `"bind_group_name"`. `Some("bind_group_name")`
and `None` will still work, but `Some("bind_group_name")` can optionally
be simplified to just `"bind_group_name"`.

---------

Co-authored-by: IceSentry <[email protected]>
ameknite pushed a commit to ameknite/bevy that referenced this pull request Nov 6, 2023
# Objective

Simplify bind group creation code. alternative to (and based on) bevyengine#9476

## Solution

- Add a `BindGroupEntries` struct that can transparently be used where
`&[BindGroupEntry<'b>]` is required in BindGroupDescriptors.

Allows constructing the descriptor's entries as:
```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::with_indexes((
        (2, &my_sampler),
        (3, my_uniform),
    )),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 2,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 3,
            resource: my_uniform,
        },
    ],
);
```

or

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::sequential((&my_sampler, my_uniform)),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 0,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 1,
            resource: my_uniform,
        },
    ],
);
```

the structs has no user facing macros, is tuple-type-based so stack
allocated, and has no noticeable impact on compile time.

- Also adds a `DynamicBindGroupEntries` struct with a similar api that
uses a `Vec` under the hood and allows extending the entries.
- Modifies `RenderDevice::create_bind_group` to take separate arguments
`label`, `layout` and `entries` instead of a `BindGroupDescriptor`
struct. The struct can't be stored due to the internal references, and
with only 3 members arguably does not add enough context to justify
itself.
- Modify the codebase to use the new api and the `BindGroupEntries` /
`DynamicBindGroupEntries` structs where appropriate (whenever the
entries slice contains more than 1 member).

## Migration Guide

- Calls to `RenderDevice::create_bind_group({BindGroupDescriptor {
label, layout, entries })` must be amended to
`RenderDevice::create_bind_group(label, layout, entries)`.
- If `label`s have been specified as `"bind_group_name".into()`, they
need to change to just `"bind_group_name"`. `Some("bind_group_name")`
and `None` will still work, but `Some("bind_group_name")` can optionally
be simplified to just `"bind_group_name"`.

---------

Co-authored-by: IceSentry <[email protected]>
github-merge-queue bot pushed a commit that referenced this pull request Nov 28, 2023
# Objective

- Follow up to #9694

## Solution

- Same api as #9694 but adapted for `BindGroupLayoutEntry`
- Use the same `ShaderStages` visibilty for all entries by default
- Add `BindingType` helper function that mirror the wgsl equivalent and
that make writing layouts much simpler.

Before:
```rust
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
    label: Some("post_process_bind_group_layout"),
    entries: &[
        BindGroupLayoutEntry {
            binding: 0,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Texture {
                sample_type: TextureSampleType::Float { filterable: true },
                view_dimension: TextureViewDimension::D2,
                multisampled: false,
            },
            count: None,
        },
        BindGroupLayoutEntry {
            binding: 1,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Sampler(SamplerBindingType::Filtering),
            count: None,
        },
        BindGroupLayoutEntry {
            binding: 2,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Buffer {
                ty: bevy::render::render_resource::BufferBindingType::Uniform,
                has_dynamic_offset: false,
                min_binding_size: Some(PostProcessSettings::min_size()),
            },
            count: None,
        },
    ],
});
```
After:
```rust
let layout = render_device.create_bind_group_layout(
    "post_process_bind_group_layout"),
    &BindGroupLayoutEntries::sequential(
        ShaderStages::FRAGMENT,
        (
            texture_2d_f32(),
            sampler(SamplerBindingType::Filtering),
            uniform_buffer(false, Some(PostProcessSettings::min_size())),
        ),
    ),
);
```

Here's a more extreme example in bevy_solari:
JMS55@86dab7f

---

## Changelog

- Added `BindGroupLayoutEntries` and all `BindingType` helper functions.

## Migration Guide

`RenderDevice::create_bind_group_layout()` doesn't take a
`BindGroupLayoutDescriptor` anymore. You need to provide the parameters
separately

```rust
// 0.12
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
    label: Some("post_process_bind_group_layout"),
    entries: &[
        BindGroupLayoutEntry {
			// ...
        },
    ],
});

// 0.13
let layout = render_device.create_bind_group_layout(
	"post_process_bind_group_layout",
    &[
        BindGroupLayoutEntry {
			// ...
        },
    ],
);
```

## TODO

- [x] implement a `Dynamic` variant
- [x] update the `RenderDevice::create_bind_group_layout()` api to match
the one from `RenderDevice::creat_bind_group()`
- [x] docs
rdrpenguin04 pushed a commit to rdrpenguin04/bevy that referenced this pull request Jan 9, 2024
# Objective

Simplify bind group creation code. alternative to (and based on) bevyengine#9476

## Solution

- Add a `BindGroupEntries` struct that can transparently be used where
`&[BindGroupEntry<'b>]` is required in BindGroupDescriptors.

Allows constructing the descriptor's entries as:
```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::with_indexes((
        (2, &my_sampler),
        (3, my_uniform),
    )),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 2,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 3,
            resource: my_uniform,
        },
    ],
);
```

or

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::sequential((&my_sampler, my_uniform)),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 0,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 1,
            resource: my_uniform,
        },
    ],
);
```

the structs has no user facing macros, is tuple-type-based so stack
allocated, and has no noticeable impact on compile time.

- Also adds a `DynamicBindGroupEntries` struct with a similar api that
uses a `Vec` under the hood and allows extending the entries.
- Modifies `RenderDevice::create_bind_group` to take separate arguments
`label`, `layout` and `entries` instead of a `BindGroupDescriptor`
struct. The struct can't be stored due to the internal references, and
with only 3 members arguably does not add enough context to justify
itself.
- Modify the codebase to use the new api and the `BindGroupEntries` /
`DynamicBindGroupEntries` structs where appropriate (whenever the
entries slice contains more than 1 member).

## Migration Guide

- Calls to `RenderDevice::create_bind_group({BindGroupDescriptor {
label, layout, entries })` must be amended to
`RenderDevice::create_bind_group(label, layout, entries)`.
- If `label`s have been specified as `"bind_group_name".into()`, they
need to change to just `"bind_group_name"`. `Some("bind_group_name")`
and `None` will still work, but `Some("bind_group_name")` can optionally
be simplified to just `"bind_group_name"`.

---------

Co-authored-by: IceSentry <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Code-Quality A section of code that is hard to understand or change S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants