Skip to content

[Merged by Bors] - ExtractComponent output optional associated type#6699

Closed
torsteingrindvik wants to merge 8 commits into
bevyengine:mainfrom
torsteingrindvik:extract-component
Closed

[Merged by Bors] - ExtractComponent output optional associated type#6699
torsteingrindvik wants to merge 8 commits into
bevyengine:mainfrom
torsteingrindvik:extract-component

Conversation

@torsteingrindvik

@torsteingrindvik torsteingrindvik commented Nov 20, 2022

Copy link
Copy Markdown
Contributor

Objective

Allow more use cases where the user may benefit from both ExtractComponentPlugin and UniformComponentPlugin.

Solution

Add an associated type to ExtractComponent in order to allow specifying the output component (or bundle).

Make extract_component return an Option<_> such that components can be extracted only when needed.

What problem does this solve?

ExtractComponentPlugin allows extracting components, but currently the output type is the same as the input.
This means that use cases such as having a settings struct which turns into a uniform is awkward.

For example we might have:

struct MyStruct {
    enabled: bool,
    val: f32
}

struct MyStructUniform {
    val: f32
}

With the new approach, we can extract MyStruct only when it is enabled, and turn it into its related uniform.

This chains well with UniformComponentPlugin.

The user may then:

app.add_plugin(ExtractComponentPlugin::<MyStruct>::default());
app.add_plugin(UniformComponentPlugin::<MyStructUniform>::default());

This then saves the user a fair amount of boilerplate.

Changelog

Changed

  • ExtractComponent can specify output type, and outputting is optional.

Add an associated type to `ExtractComponent` in order to allow specifying
the output component.

Make `extract_component` return an `Option<_>` such that components can
be extracted only when needed.

What problem does this solve?

`ExtractComponentPlugin` allows extracting components, but currently the
output type is the same as the input.
This means that use cases such as having a settings struct which turns
into a uniform is awkward.

For example we might have:

```rust
struct MyStruct {
    enabled: bool,
    val: f32
}

struct MyStructUniform {
    val: f32
}
```

With the new approach, we can extract `MyStruct` only when it is
enabled, and turn it into its related uniform.

This chains well with `UniformComponentPlugin`.

The user may then:

```rust
app.add_plugin(ExtractComponentPlugin::<MyStruct>::default());
app.add_plugin(UniformComponentPlugin::<MyStructUniform>::default());
```

This then saves the user a fair amount of boilerplate.

Signed-off-by: Torstein Grindvik <torstein.grindvik@nordicsemi.no>
Signed-off-by: Torstein Grindvik <torstein.grindvik@nordicsemi.no>
@torsteingrindvik

Copy link
Copy Markdown
Contributor Author

Added a second commit which uses the changes on the bloom plugin.

The results are:

  • Bloom reuses ExtractComponentPlugin
  • Bloom reuses UniformComponentPlugin
  • Eliminated a struct: BloomUniforms
  • Eliminated a struct: BloomUniformIndex
  • Eliminated a system: extract_bloom_settings
  • Eliminated a system: prepare_bloom_uniforms

Signed-off-by: Torstein Grindvik <torstein.grindvik@nordicsemi.no>
#[derive(ShaderType)]
struct BloomUniform {
#[derive(Component, ShaderType, Clone)]
pub struct BloomUniform {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doc strings :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a docstring.

Comment thread crates/bevy_render/src/extract_component.rs Outdated
Comment thread crates/bevy_render/src/extract_component.rs Outdated

/// Defines how the component is transferred into the "render world".
fn extract_component(item: QueryItem<'_, Self::Query>) -> Self;
fn extract_component(item: QueryItem<'_, Self::Query>) -> Option<Self::Out>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want to add a Clone trait bound and add a default impl here? Some(item.clone()) seems to be the common pattern.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Extracting uncloneable components generally seems impossible or at least poorly thought out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I'm not too sure.

After these changes, the output type can be something entirely different than what is queried.
So a Clone bound on Self in that case is not strictly needed.

torsteingrindvik and others added 3 commits November 20, 2022 15:42
Signed-off-by: Torstein Grindvik <torstein.grindvik@nordicsemi.no>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>

@alice-i-cecile alice-i-cecile left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The lack of associated type defaults sucks, but this is still an improvement.

@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Nov 20, 2022
type Out = BloomUniform;

fn extract_component((settings, camera): QueryItem<'_, Self::Query>) -> Option<Self::Out> {
if let Some(size) = camera.physical_viewport_size() {

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.

Replace if-let with Option::map.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

cameras: Extract<Query<(Entity, &Camera, &BloomSettings), With<Camera>>>,
) {
for (entity, camera, bloom_settings) in &cameras {
if camera.is_active && camera.hdr {

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.

Now that you removed this, where are you checking for these conditions? Did I miss this somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, that was my bad.
Added that check in the extract component impl for BloomSettings

/// The uniform struct extracted from [`BloomSettings`] attached to a [`Camera`].
/// Will be available for use in the Bloom shader.
#[derive(Component, ShaderType, Clone)]
pub struct BloomUniform {

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.

Can we keep this struct private? I don't like exposing bloom implementation details.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'd like that too, but by doing impl ExtractComponent on something public like BloomSettings and having the Out type be BloomUniform means that BloomUniform too must be public (else compile error)

@alice-i-cecile alice-i-cecile Nov 20, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can use #[doc(hidden)] to reduce public visibility.

@JMS55 JMS55 Nov 20, 2022

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.

I don't love this, but I think the reduction in boilerplate is worth it, given that we can somewhat mitigate it with doc hidden (does anyone know if rust analyzer ignores hidden items when it comes to autocomplete?). I'm good with these changes once doc hidden is added :).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added the hidden attr now.
I kept the docstring anyway since I saw that was done in other instances of hiding pub items in Bevy.

Signed-off-by: Torstein Grindvik <torstein.grindvik@nordicsemi.no>
Signed-off-by: Torstein Grindvik <torstein.grindvik@nordicsemi.no>

@JMS55 JMS55 left a comment

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.

lgtm!

@james7132 james7132 added this to the 0.10 milestone Nov 20, 2022
@james7132 james7132 added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Nov 20, 2022
@alice-i-cecile

Copy link
Copy Markdown
Member

bors r+

bors Bot pushed a commit that referenced this pull request Nov 21, 2022
# Objective

Allow more use cases where the user may benefit from both `ExtractComponentPlugin` _and_ `UniformComponentPlugin`.

## Solution

Add an associated type to `ExtractComponent` in order to allow specifying the output component (or bundle).

Make `extract_component` return an `Option<_>` such that components can be extracted only when needed.

What problem does this solve?

`ExtractComponentPlugin` allows extracting components, but currently the output type is the same as the input.
This means that use cases such as having a settings struct which turns into a uniform is awkward.

For example we might have:

```rust
struct MyStruct {
    enabled: bool,
    val: f32
}

struct MyStructUniform {
    val: f32
}
```

With the new approach, we can extract `MyStruct` only when it is enabled, and turn it into its related uniform.

This chains well with `UniformComponentPlugin`.

The user may then:

```rust
app.add_plugin(ExtractComponentPlugin::<MyStruct>::default());
app.add_plugin(UniformComponentPlugin::<MyStructUniform>::default());
```

This then saves the user a fair amount of boilerplate.


## Changelog

### Changed

- `ExtractComponent` can specify output type, and outputting is optional.



Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
@bors bors Bot changed the title ExtractComponent output optional associated type [Merged by Bors] - ExtractComponent output optional associated type Nov 21, 2022
@bors bors Bot closed this Nov 21, 2022
alradish pushed a commit to alradish/bevy that referenced this pull request Jan 22, 2023
# Objective

Allow more use cases where the user may benefit from both `ExtractComponentPlugin` _and_ `UniformComponentPlugin`.

## Solution

Add an associated type to `ExtractComponent` in order to allow specifying the output component (or bundle).

Make `extract_component` return an `Option<_>` such that components can be extracted only when needed.

What problem does this solve?

`ExtractComponentPlugin` allows extracting components, but currently the output type is the same as the input.
This means that use cases such as having a settings struct which turns into a uniform is awkward.

For example we might have:

```rust
struct MyStruct {
    enabled: bool,
    val: f32
}

struct MyStructUniform {
    val: f32
}
```

With the new approach, we can extract `MyStruct` only when it is enabled, and turn it into its related uniform.

This chains well with `UniformComponentPlugin`.

The user may then:

```rust
app.add_plugin(ExtractComponentPlugin::<MyStruct>::default());
app.add_plugin(UniformComponentPlugin::<MyStructUniform>::default());
```

This then saves the user a fair amount of boilerplate.


## Changelog

### Changed

- `ExtractComponent` can specify output type, and outputting is optional.



Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Allow more use cases where the user may benefit from both `ExtractComponentPlugin` _and_ `UniformComponentPlugin`.

## Solution

Add an associated type to `ExtractComponent` in order to allow specifying the output component (or bundle).

Make `extract_component` return an `Option<_>` such that components can be extracted only when needed.

What problem does this solve?

`ExtractComponentPlugin` allows extracting components, but currently the output type is the same as the input.
This means that use cases such as having a settings struct which turns into a uniform is awkward.

For example we might have:

```rust
struct MyStruct {
    enabled: bool,
    val: f32
}

struct MyStructUniform {
    val: f32
}
```

With the new approach, we can extract `MyStruct` only when it is enabled, and turn it into its related uniform.

This chains well with `UniformComponentPlugin`.

The user may then:

```rust
app.add_plugin(ExtractComponentPlugin::<MyStruct>::default());
app.add_plugin(UniformComponentPlugin::<MyStructUniform>::default());
```

This then saves the user a fair amount of boilerplate.


## Changelog

### Changed

- `ExtractComponent` can specify output type, and outputting is optional.



Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
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-Usability A targeted quality-of-life change that makes Bevy easier to use S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants