diff --git a/crates/bevy_pbr/src/material_bind_groups.rs b/crates/bevy_pbr/src/material_bind_groups.rs index 5fdf603c26..3baa25eb9a 100644 --- a/crates/bevy_pbr/src/material_bind_groups.rs +++ b/crates/bevy_pbr/src/material_bind_groups.rs @@ -19,7 +19,6 @@ use bevy_render::{ UnpreparedBindGroup, WgpuSampler, WgpuTextureView, }, renderer::RenderDevice, - settings::WgpuFeatures, texture::FallbackImage, }; use bevy_utils::{default, tracing::error, HashMap}; @@ -795,10 +794,7 @@ pub fn material_uses_bindless_resources(render_device: &RenderDevice) -> bool where M: Material, { - M::bindless_slot_count().is_some() - && render_device - .features() - .contains(WgpuFeatures::BUFFER_BINDING_ARRAY | WgpuFeatures::TEXTURE_BINDING_ARRAY) + M::bindless_slot_count().is_some() && M::bindless_supported(render_device) } impl FromWorld for FallbackBindlessResources { diff --git a/crates/bevy_render/macros/src/as_bind_group.rs b/crates/bevy_render/macros/src/as_bind_group.rs index 96176e071c..d5ce58d46c 100644 --- a/crates/bevy_render/macros/src/as_bind_group.rs +++ b/crates/bevy_render/macros/src/as_bind_group.rs @@ -132,6 +132,11 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { } }; + // Count the number of sampler fields needed. We might have to disable + // bindless if bindless arrays take the GPU over the maximum number of + // samplers. + let mut sampler_binding_count = 0; + // Read field-level attributes for field in fields { // 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 { ) }); + sampler_binding_count += 1; + binding_layouts.push(quote! { #render_path::render_resource::BindGroupLayoutEntry { binding: #binding_index, @@ -417,6 +424,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { ) }); + sampler_binding_count += 1; + binding_layouts.push(quote!{ #render_path::render_resource::BindGroupLayoutEntry { binding: #binding_index, @@ -440,11 +449,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { Some(_) => { quote! { let (#uniform_binding_type, #uniform_buffer_usages) = - if render_device.features().contains( - #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | - #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY - ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 && - !force_no_bindless { + if Self::bindless_supported(render_device) && !force_no_bindless { ( #render_path::render_resource::BufferBindingType::Storage { read_only: true }, #render_path::render_resource::BufferUsages::STORAGE, @@ -563,6 +568,17 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { (prepared_data.clone(), prepared_data) }; + // Calculate the number of samplers that we need, so that we don't go over + // the limit on certain platforms. See + // https://github.com/bevyengine/bevy/issues/16988. + let samplers_needed = match attr_bindless_count { + Some(Lit::Int(ref bindless_count)) => match bindless_count.base10_parse::() { + Ok(bindless_count) => sampler_binding_count * bindless_count, + Err(_) => 0, + }, + _ => 0, + }; + // Calculate the actual number of bindless slots, taking hardware // limitations into account. 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 { fn bindless_slot_count() -> Option { Some(#bindless_count) } + + fn bindless_supported(render_device: &#render_path::renderer::RenderDevice) -> bool { + render_device.features().contains( + #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | + #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY + ) && + render_device.limits().max_storage_buffers_per_shader_stage > 0 && + render_device.limits().max_samplers_per_shader_stage >= #samplers_needed + } }, quote! { - let #actual_bindless_slot_count = if render_device.features().contains( - #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | - #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY - ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 && - !force_no_bindless { + let #actual_bindless_slot_count = if Self::bindless_supported(render_device) && + !force_no_bindless { ::core::num::NonZeroU32::new(#bindless_count) } else { None diff --git a/crates/bevy_render/src/render_resource/bind_group.rs b/crates/bevy_render/src/render_resource/bind_group.rs index 8d0ed47f83..568a2dc811 100644 --- a/crates/bevy_render/src/render_resource/bind_group.rs +++ b/crates/bevy_render/src/render_resource/bind_group.rs @@ -342,6 +342,15 @@ pub trait AsBindGroup { None } + /// True if the hardware *actually* supports bindless textures for this + /// type, taking the device and driver capabilities into account. + /// + /// If this type doesn't use bindless textures, then the return value from + /// this function is meaningless. + fn bindless_supported(_: &RenderDevice) -> bool { + true + } + /// label fn label() -> Option<&'static str> { None