-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Add bindless support back to ExtendedMaterial
.
#18025
Conversation
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.
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 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. |
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 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.
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 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.
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 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.
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.
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.
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.
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
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.
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.
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.
That would be PR #18771 if you want to see an example of where we need the range
field.
They were sized based on the total number of resources (base + extended), which was incorrect. They should be based on the binding range.
Updated to main and fixed an issue with the bindless index table sizes. |
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.
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
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.
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.
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.
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))] |
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 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".
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 don't like it any more than you do :) And I agree. We discussed some ideas for improvement on Discord.
…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>
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.
…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>
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.