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,
|
UnpreparedBindGroup, WgpuSampler, WgpuTextureView,
|
||||||
},
|
},
|
||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
settings::WgpuFeatures,
|
|
||||||
texture::FallbackImage,
|
texture::FallbackImage,
|
||||||
};
|
};
|
||||||
use bevy_utils::{default, tracing::error, HashMap};
|
use bevy_utils::{default, tracing::error, HashMap};
|
||||||
@ -795,10 +794,7 @@ pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
|
|||||||
where
|
where
|
||||||
M: Material,
|
M: Material,
|
||||||
{
|
{
|
||||||
M::bindless_slot_count().is_some()
|
M::bindless_slot_count().is_some() && M::bindless_supported(render_device)
|
||||||
&& render_device
|
|
||||||
.features()
|
|
||||||
.contains(WgpuFeatures::BUFFER_BINDING_ARRAY | WgpuFeatures::TEXTURE_BINDING_ARRAY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromWorld for FallbackBindlessResources {
|
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
|
// Read field-level attributes
|
||||||
for field in fields {
|
for field in fields {
|
||||||
// Search ahead for texture attributes so we can use them with any
|
// 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! {
|
binding_layouts.push(quote! {
|
||||||
#render_path::render_resource::BindGroupLayoutEntry {
|
#render_path::render_resource::BindGroupLayoutEntry {
|
||||||
binding: #binding_index,
|
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!{
|
binding_layouts.push(quote!{
|
||||||
#render_path::render_resource::BindGroupLayoutEntry {
|
#render_path::render_resource::BindGroupLayoutEntry {
|
||||||
binding: #binding_index,
|
binding: #binding_index,
|
||||||
@ -440,11 +449,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
|
|||||||
Some(_) => {
|
Some(_) => {
|
||||||
quote! {
|
quote! {
|
||||||
let (#uniform_binding_type, #uniform_buffer_usages) =
|
let (#uniform_binding_type, #uniform_buffer_usages) =
|
||||||
if render_device.features().contains(
|
if Self::bindless_supported(render_device) && !force_no_bindless {
|
||||||
#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 {
|
|
||||||
(
|
(
|
||||||
#render_path::render_resource::BufferBindingType::Storage { read_only: true },
|
#render_path::render_resource::BufferBindingType::Storage { read_only: true },
|
||||||
#render_path::render_resource::BufferUsages::STORAGE,
|
#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)
|
(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
|
// Calculate the actual number of bindless slots, taking hardware
|
||||||
// limitations into account.
|
// limitations into account.
|
||||||
let (bindless_slot_count, actual_bindless_slot_count_declaration) = match attr_bindless_count {
|
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> {
|
fn bindless_slot_count() -> Option<u32> {
|
||||||
Some(#bindless_count)
|
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! {
|
quote! {
|
||||||
let #actual_bindless_slot_count = if render_device.features().contains(
|
let #actual_bindless_slot_count = if Self::bindless_supported(render_device) &&
|
||||||
#render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY |
|
!force_no_bindless {
|
||||||
#render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY
|
|
||||||
) && render_device.limits().max_storage_buffers_per_shader_stage > 0 &&
|
|
||||||
!force_no_bindless {
|
|
||||||
::core::num::NonZeroU32::new(#bindless_count)
|
::core::num::NonZeroU32::new(#bindless_count)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -342,6 +342,15 @@ pub trait AsBindGroup {
|
|||||||
None
|
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
|
/// label
|
||||||
fn label() -> Option<&'static str> {
|
fn label() -> Option<&'static str> {
|
||||||
None
|
None
|
||||||
|
Loading…
Reference in New Issue
Block a user