Skip to content

Commit 0e36abc

Browse files
authored
Disable bindless on a per-material basis if the specific material uses more samplers than are available on the device. (#17155)
Some hardware and driver combos, such as Intel Iris Xe, have low limits on the numbers of samplers per shader, causing an overflow. With first-class bindless arrays, `wgpu` should be able to work around this limitation eventually, but for now we need to disable bindless materials on those platforms. This is an alternative to PR #17107 that calculates the precise number of samplers needed and compares to the hardware sampler limit, transparently falling back to non-bindless if the limit is exceeded. Fixes #16988.
1 parent 3c829d7 commit 0e36abc

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

crates/bevy_pbr/src/material_bind_groups.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use bevy_render::{
1919
UnpreparedBindGroup, WgpuSampler, WgpuTextureView,
2020
},
2121
renderer::RenderDevice,
22-
settings::WgpuFeatures,
2322
texture::FallbackImage,
2423
};
2524
use bevy_utils::{default, tracing::error, HashMap};
@@ -795,10 +794,7 @@ pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
795794
where
796795
M: Material,
797796
{
798-
M::bindless_slot_count().is_some()
799-
&& render_device
800-
.features()
801-
.contains(WgpuFeatures::BUFFER_BINDING_ARRAY | WgpuFeatures::TEXTURE_BINDING_ARRAY)
797+
M::bindless_slot_count().is_some() && M::bindless_supported(render_device)
802798
}
803799

804800
impl FromWorld for FallbackBindlessResources {

crates/bevy_render/macros/src/as_bind_group.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
132132
}
133133
};
134134

135+
// Count the number of sampler fields needed. We might have to disable
136+
// bindless if bindless arrays take the GPU over the maximum number of
137+
// samplers.
138+
let mut sampler_binding_count = 0;
139+
135140
// Read field-level attributes
136141
for field in fields {
137142
// Search ahead for texture attributes so we can use them with any
@@ -341,6 +346,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
341346
)
342347
});
343348

349+
sampler_binding_count += 1;
350+
344351
binding_layouts.push(quote! {
345352
#render_path::render_resource::BindGroupLayoutEntry {
346353
binding: #binding_index,
@@ -417,6 +424,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
417424
)
418425
});
419426

427+
sampler_binding_count += 1;
428+
420429
binding_layouts.push(quote!{
421430
#render_path::render_resource::BindGroupLayoutEntry {
422431
binding: #binding_index,
@@ -440,11 +449,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
440449
Some(_) => {
441450
quote! {
442451
let (#uniform_binding_type, #uniform_buffer_usages) =
443-
if render_device.features().contains(
444-
#render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY |
445-
#render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY
446-
) && render_device.limits().max_storage_buffers_per_shader_stage > 0 &&
447-
!force_no_bindless {
452+
if Self::bindless_supported(render_device) && !force_no_bindless {
448453
(
449454
#render_path::render_resource::BufferBindingType::Storage { read_only: true },
450455
#render_path::render_resource::BufferUsages::STORAGE,
@@ -563,6 +568,17 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
563568
(prepared_data.clone(), prepared_data)
564569
};
565570

571+
// Calculate the number of samplers that we need, so that we don't go over
572+
// the limit on certain platforms. See
573+
// https://github.com/bevyengine/bevy/issues/16988.
574+
let samplers_needed = match attr_bindless_count {
575+
Some(Lit::Int(ref bindless_count)) => match bindless_count.base10_parse::<u32>() {
576+
Ok(bindless_count) => sampler_binding_count * bindless_count,
577+
Err(_) => 0,
578+
},
579+
_ => 0,
580+
};
581+
566582
// Calculate the actual number of bindless slots, taking hardware
567583
// limitations into account.
568584
let (bindless_slot_count, actual_bindless_slot_count_declaration) = match attr_bindless_count {
@@ -571,13 +587,19 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
571587
fn bindless_slot_count() -> Option<u32> {
572588
Some(#bindless_count)
573589
}
590+
591+
fn bindless_supported(render_device: &#render_path::renderer::RenderDevice) -> bool {
592+
render_device.features().contains(
593+
#render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY |
594+
#render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY
595+
) &&
596+
render_device.limits().max_storage_buffers_per_shader_stage > 0 &&
597+
render_device.limits().max_samplers_per_shader_stage >= #samplers_needed
598+
}
574599
},
575600
quote! {
576-
let #actual_bindless_slot_count = if render_device.features().contains(
577-
#render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY |
578-
#render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY
579-
) && render_device.limits().max_storage_buffers_per_shader_stage > 0 &&
580-
!force_no_bindless {
601+
let #actual_bindless_slot_count = if Self::bindless_supported(render_device) &&
602+
!force_no_bindless {
581603
::core::num::NonZeroU32::new(#bindless_count)
582604
} else {
583605
None

crates/bevy_render/src/render_resource/bind_group.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,15 @@ pub trait AsBindGroup {
342342
None
343343
}
344344

345+
/// True if the hardware *actually* supports bindless textures for this
346+
/// type, taking the device and driver capabilities into account.
347+
///
348+
/// If this type doesn't use bindless textures, then the return value from
349+
/// this function is meaningless.
350+
fn bindless_supported(_: &RenderDevice) -> bool {
351+
true
352+
}
353+
345354
/// label
346355
fn label() -> Option<&'static str> {
347356
None

0 commit comments

Comments
 (0)