Skip to content

Add bindless support back to ExtendedMaterial. #18025

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

Merged
merged 12 commits into from
Apr 9, 2025

Conversation

pcwalton
Copy link
Contributor

PR #17898 disabled bindless support for ExtendedMaterial. This commit adds it back. It also adds a new example, extended_material_bindless, showing how to use it.

PR bevyengine#17898 disabled bindless support for `ExtendedMaterial`. This commit
adds it back. It also adds a new example, `extended_material_bindless`,
showing how to use it.
@pcwalton pcwalton added A-Rendering Drawing game state to the screen S-Needs-Review Needs reviewer attention (from anyone!) to move forward C-Feature A new feature, making something new possible labels Feb 25, 2025
Copy link
Contributor

@robtfm robtfm left a comment

Choose a reason for hiding this comment

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

the core of the implementation looks good. i think there's potential to make it a bit less confusing for users, but i'm not certain (i had to get up to speed with the base bindless implementation along the way).

///
/// As usual for material extensions, we need to avoid conflicting with both the
/// binding numbers and bindless indices of the [`StandardMaterial`], so we
/// start both values at 100 and 50 respectively.
Copy link
Contributor

Choose a reason for hiding this comment

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

i found this very hard to understand. i think the base of my confusion is that i couldn't see why we need to specify unique table indices for each item of data (the "50..53").

i think (please correct me if i'm wrong) it's for non-bindless fallback. if that's the case, then in non-bindless the uniform will be @binding(50), and in bindless @binding(101), right? it's a bit confusing, i think it would be better to use 101..=103 for example purposes, so at least the uniform is the same.

then ... if we are already specifying the uniform slot, and the individual slots for each item of data with #[texture(x)] etc, why do we need to specify the table range here at all? is there a reason to want some fields bindless and others not?

this would also bring the binding-conflict requirements to the same thing for bindless and non-bindless, which would be a bit easier to think about.

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 found this very hard to understand. i think the base of my confusion is that i couldn't see why we need to specify unique table indices for each item of data (the "50..53").

The table indices point to the slot reserved for each texture in each binding buffer. The shader accesses a texture called base_color with code like bindless_textures_2d[table.base_color[material_index]].

then ... if we are already specifying the uniform slot, and the individual slots for each item of data with #[texture(x)] etc, why do we need to specify the table range here at all? is there a reason to want some fields bindless and others not?

One reason is that the table entries are contiguous in memory, so you might want them to be smaller than the binding numbers to avoid wasting space in the table with unused bindings.

then ... if we are already specifying the uniform slot, and the individual slots for each item of data with #[texture(x)] etc, why do we need to specify the table range here at all? is there a reason to want some fields bindless and others not?

If you know you're deploying on a platform with no bindless, you might want ExtendedMaterial to not use bindless to avoid the verbosity.

Copy link
Contributor

Choose a reason for hiding this comment

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

the table indices need to be unique over the whole material, right? and bindless will only be used if all the base materials in the type stack are bindless-enabled too.

so the uniqueness requirement for the table indices is precisely the same requirement as for the slot indices in non-bindless fallback?

so if you can do better by using lower table indices, you can also just use lower slot assignments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually sorry, my explanation before was wrong. There are two tables for a bindless ExtendedMaterial: one for the base and one for the extension. This is necessary because pbr_input_from_standard_material will read from material_indices, an array of StandardMaterialBindings in pbr_bindings.wgsl. The StandardMaterialBindings have a hard-coded size. If we tried to place the standard material bindings and the extended material bindings in the same array, then the size of each element of pbr_bindings::material_bindings would be wrong, and you'd get a corrupted shader.

So the reason why we need the 50..53 bit is that we have to specify which bindings go into the second table. The indices are mapped starting at 0. So binding 50 gets mapped to struct field 0, binding 51 gets mapped to struct field 1, etc.

Copy link
Contributor

Choose a reason for hiding this comment

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

sorry but i still don't get why they need to be manually specified.

if you're comfortable i'm just missing the point then i guess i recuse myself

Copy link
Contributor Author

@pcwalton pcwalton Apr 8, 2025

Choose a reason for hiding this comment

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

Now that I think about it, we might actually not need this PR to support manually specifying the size. But we would want it for #18104 because in that case we want to specify the size of the table to be 0..31, regardless of whether the feature that enables the 30th binding is enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would be PR #18771 if you want to see an example of where we need the range field.

@pcwalton pcwalton added this to the 0.16 milestone Apr 1, 2025
pcwalton added 2 commits April 5, 2025 20:20
They were sized based on the total number of resources (base +
extended), which was incorrect. They should be based on the binding
range.
@pcwalton
Copy link
Contributor Author

pcwalton commented Apr 6, 2025

Updated to main and fixed an issue with the bindless index table sizes.

Copy link
Contributor

@BenjaminBrienen BenjaminBrienen left a comment

Choose a reason for hiding this comment

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

image

2025-04-06T19:38:17.754020Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Linux (Pop!_OS 22.04)", kernel: "6.12.10-76061203-generic", cpu: "AMD Ryzen 9 7950X 16-Core Processor", core_count: "16", memory: "61.9 GiB" }    
2025-04-06T19:38:17.900941Z  INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA GeForce RTX 3070", vendor: 4318, device: 9348, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "565.77", backend: Vulkan }
2025-04-06T19:38:18.471014Z  INFO bevy_winit::system: Creating new window extended_material_bindless (0v1)
2025-04-06T19:38:18.471114Z  INFO winit::platform_impl::linux::x11::window: Guessed window scale factor: 2
2025-04-06T19:38:18.728560Z  WARN bevy_render::view::window: Couldn't get swap chain texture after configuring. Cause: 'The underlying surface has changed, and therefore the swap chain must be updated'
2025-04-06T19:38:18.754750Z  WARN bevy_render::view::window: Couldn't get swap chain texture after configuring. Cause: 'The underlying surface has changed, and therefore the swap chain must be updated'
2025-04-06T19:38:36.461479Z  INFO bevy_window::system: No windows are open, exiting    
2025-04-06T19:38:36.462640Z  INFO bevy_winit::system: Closing window 0v1

@BenjaminBrienen BenjaminBrienen added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Apr 6, 2025
pcwalton added a commit to pcwalton/bevy that referenced this pull request Apr 8, 2025
regardless of the features that are enabled.

Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in bevyengine#18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in bevyengine#18025 to do so. Thus this patch has a dependency on bevyengine#18025.

Closes bevyengine#18104.
pcwalton added a commit to pcwalton/bevy that referenced this pull request Apr 8, 2025
regardless of the features that are enabled.

Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in bevyengine#18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in bevyengine#18025 to do so. Thus this patch has a dependency on bevyengine#18025.

Closes bevyengine#18104.
pcwalton added a commit to pcwalton/bevy that referenced this pull request Apr 9, 2025
regardless of the features that are enabled.

Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in bevyengine#18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in bevyengine#18025 to do so. Thus this patch has a dependency on bevyengine#18025.

Closes bevyengine#18104.
@pcwalton
Copy link
Contributor Author

pcwalton commented Apr 9, 2025

I switched the texture to the UV checkerboard pattern. Now the example looks significantly less ugly.

/// Thus we need to specify a different binding so that our extended bindless
/// index table doesn't conflict.
#[derive(Asset, Clone, Reflect, AsBindGroup)]
#[data(50, ExampleBindlessExtensionUniform, binding_array(101))]
Copy link
Member

Choose a reason for hiding this comment

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

The level of complexity this foists on users is pretty gnarly (both here and in shaders). Getting these indices right feels like "unnecessary implementation details".

It definitely feels like its time to start building a higher level system that abstracts over data binding and access, removing the need for developers to handle it manually. It would be very nice if "bindless" was automatic and transparent on both the rust side and the shader side (at least by default).

As a shader developer just trying to be creative (ex: I'm working on a water system in a side project at the moment), and as someone not intimately familiar with the bindless impl (which will be the majority of our shader authors), I see this complexity and think "I'll pass and eat the overhead".

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 don't like it any more than you do :) And I agree. We discussed some ideas for improvement on Discord.

@cart cart added this pull request to the merge queue Apr 9, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Apr 9, 2025
@superdump superdump added this pull request to the merge queue Apr 9, 2025
Merged via the queue into bevyengine:main with commit dc7c8f2 Apr 9, 2025
36 checks passed
@github-project-automation github-project-automation bot moved this to Done in Rendering Apr 9, 2025
github-merge-queue bot pushed a commit that referenced this pull request Apr 9, 2025
…gardless of the features that are enabled. (#18771)

Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in #18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in #18025 to do so. Thus this patch has a dependency on #18025.

Closes #18104.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
mockersf pushed a commit that referenced this pull request Apr 9, 2025
PR #17898 disabled bindless support for `ExtendedMaterial`. This commit
adds it back. It also adds a new example, `extended_material_bindless`,
showing how to use it.
mockersf pushed a commit that referenced this pull request Apr 9, 2025
…gardless of the features that are enabled. (#18771)

Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in #18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in #18025 to do so. Thus this patch has a dependency on #18025.

Closes #18104.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.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-Feature A new feature, making something new possible S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

6 participants