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.
This commit is contained in:
parent
3c829d7f68
commit
0e36abc180
@ -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<M>(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 {
|
||||
|
@ -132,6 +132,11 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
|
||||
}
|
||||
};
|
||||
|
||||
// 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<TokenStream> {
|
||||
)
|
||||
});
|
||||
|
||||
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<TokenStream> {
|
||||
)
|
||||
});
|
||||
|
||||
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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
(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::<u32>() {
|
||||
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<TokenStream> {
|
||||
fn bindless_slot_count() -> Option<u32> {
|
||||
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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user